<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 7.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.ico">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha256-wiz7ZSCn/btzhjKDQBms9Hx4sSeUYsDrTLg7roPstac=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.css" integrity="sha256-6cQIC71/iBIYXFK+0RHAvwmjwWzkWd+r7v/BX3/vZDc=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/themes/green/pace-theme-minimal.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/pace.min.js" integrity="sha256-gqd7YTjg/BtfqWSwsJOvndl0Bxc8gFImLEkXQT8+qj0=" crossorigin="anonymous"></script>

<script class="next-config" data-name="main" type="application/json">{"hostname":"sumumm.github.io","root":"/","images":"/images","scheme":"Gemini","darkmode":false,"version":"8.19.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":true,"style":"mac"},"fold":{"enable":true,"height":300},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":true,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":true,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta name="description" content="属性文件是什么？有什么用？怎么用？若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:type" content="article">
<meta property="og:title" content="LV06-04-linux设备模型-05-属性文件">
<meta property="og:url" content="https://sumumm.github.io/post/3cca4963.html">
<meta property="og:site_name" content="苏木">
<meta property="og:description" content="属性文件是什么？有什么用？怎么用？若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107095722933.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107104840696.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107164518539.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107164601577.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107164739343.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107165743466.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107165920554.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107172843740.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108104459756.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108104653640.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108104908429.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108105043285.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108105249577.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108151909652.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108160633745.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108161846238.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108162946044.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108163730977.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108185243172.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108185515880.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108185904025.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108185957935.png">
<meta property="article:published_time" content="2025-01-08T14:22:56.000Z">
<meta property="article:modified_time" content="2025-06-13T16:25:57.051Z">
<meta property="article:author" content="苏木">
<meta property="article:tag" content="LV06-驱动开发">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107095722933.png">


<link rel="canonical" href="https://sumumm.github.io/post/3cca4963.html">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":"","permalink":"https://sumumm.github.io/post/3cca4963.html","path":"post/3cca4963.html","title":"LV06-04-linux设备模型-05-属性文件"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>LV06-04-linux设备模型-05-属性文件 | 苏木</title>
  








    <script src="/js/browser_tools_disable.js"></script>

  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
<!-- hexo injector head_end start --><link rel="stylesheet" href="https://unpkg.com/hexo-next-tags-plus@latest/lib/tag_plus.css" media="defer" onload="this.media='all'"><!-- hexo injector head_end end --></head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">苏木</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">我的学习之路</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>苏木的家</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类页<span class="badge">42</span></a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档页<span class="badge">673</span></a></li><li class="menu-item menu-item-flink"><a href="/flink/" rel="section"><i class="fa fa-link fa-fw"></i>友人帐</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于我</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F"><span class="nav-text">一、属性文件是什么？</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6%E7%AE%80%E4%BB%8B"><span class="nav-text">1. 属性文件简介</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84"><span class="nav-text">2. 数据结构</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-struct-attribute"><span class="nav-text">2.1 struct attribute</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-struct-bin-attribute"><span class="nav-text">2.2 struct bin_attribute</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-struct-attribute-group"><span class="nav-text">2.3 struct attribute_group</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-4-struct-kobj-attribute"><span class="nav-text">2.4 struct kobj_attribute</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E7%9B%B8%E5%85%B3API"><span class="nav-text">3. 相关API</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-sysfs-create-file"><span class="nav-text">3.1 sysfs_create_file()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-sysfs-create-files"><span class="nav-text">3.2 sysfs_create_files()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-3-sysfs-remove-file"><span class="nav-text">3.3 sysfs_remove_file()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-4-sysfs-remove-files"><span class="nav-text">3.4 sysfs_remove_files()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-5-sysfs-create-group"><span class="nav-text">3.5 sysfs_create_group()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-6-ATTR"><span class="nav-text">3.6 __ATTR()</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6%E7%9A%84%E5%85%B8%E5%9E%8B%E7%94%A8%E4%BE%8B"><span class="nav-text">4. 属性文件的典型用例</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6%E7%9A%84%E5%88%9B%E5%BB%BA%E4%B8%8E%E8%B0%83%E7%94%A8%E9%80%BB%E8%BE%91"><span class="nav-text">二、属性文件的创建与调用逻辑</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-sysfs-create-file"><span class="nav-text">1. sysfs_create_file()</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-sysfs-create-file-ns"><span class="nav-text">1.1 sysfs_create_file_ns()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-sysfs-add-file-mode-ns"><span class="nav-text">1.2 sysfs_add_file_mode_ns()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-%E6%80%BB%E7%BB%93"><span class="nav-text">1.3 总结</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6%E7%9A%84%E8%AF%BB%E5%86%99%E9%80%BB%E8%BE%91"><span class="nav-text">2. 属性文件的读写逻辑</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-sysfs-kf-seq-show"><span class="nav-text">2.1 sysfs_kf_seq_show()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-sysfs-file-ops"><span class="nav-text">2.2 sysfs_file_ops()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-%E6%80%BB%E7%BB%93"><span class="nav-text">2.3 总结</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%89%E3%80%81%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6%E7%9A%84%E8%AF%BB%E5%86%99%E5%AE%9E%E7%8E%B0%E4%B8%8E%E4%BC%98%E5%8C%96"><span class="nav-text">三、属性文件的读写实现与优化</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6%E5%88%9B%E5%BB%BAdemo"><span class="nav-text">1. 属性文件创建demo</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-demo%E6%BA%90%E7%A0%81"><span class="nav-text">1.1 demo源码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-%E5%BC%80%E5%8F%91%E6%9D%BF%E9%AA%8C%E8%AF%81"><span class="nav-text">1.2 开发板验证</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E5%B0%86%E5%B1%9E%E6%80%A7%E6%94%BE%E5%85%A5ktype"><span class="nav-text">2. 将属性放入ktype</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-demo%E6%BA%90%E7%A0%81"><span class="nav-text">2.1 demo源码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-2-%E5%BC%80%E5%8F%91%E6%9D%BF%E9%AA%8C%E8%AF%81"><span class="nav-text">2.2 开发板验证</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E9%80%9A%E8%BF%87-ATTR-%E5%AE%8F%E4%BC%98%E5%8C%96%E4%B8%80%E4%B8%8B"><span class="nav-text">3. 通过__ATTR() 宏优化一下</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-%E6%80%8E%E4%B9%88%E4%BC%98%E5%8C%96%EF%BC%9F"><span class="nav-text">3.1 怎么优化？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-2-demo%E6%BA%90%E7%A0%81"><span class="nav-text">3.2 demo源码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-3-%E5%BC%80%E5%8F%91%E6%9D%BF%E9%AA%8C%E8%AF%81"><span class="nav-text">3.3 开发板验证</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4-%E9%BB%98%E8%AE%A4%E5%B1%9E%E6%80%A7%EF%BC%9F"><span class="nav-text">4. 默认属性？</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#4-1-%E6%9C%89%E4%B8%80%E7%A7%8D%E5%88%9B%E5%BB%BA%E6%96%B9%E5%BC%8F%E6%B2%A1%E6%9C%89%E6%8C%87%E5%AE%9Aktype%EF%BC%9F"><span class="nav-text">4.1 有一种创建方式没有指定ktype？</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-2-demo%E6%BA%90%E7%A0%81"><span class="nav-text">4.2 demo源码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-3-%E5%BC%80%E5%8F%91%E6%9D%BF%E9%AA%8C%E8%AF%81"><span class="nav-text">4.3 开发板验证</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#4-4-%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E8%83%BD%E5%B0%81%E8%A3%85%E5%88%B0%E4%B8%80%E8%B5%B7%EF%BC%9F"><span class="nav-text">4.4 为什么不能封装到一起？</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5-%E5%88%9B%E5%BB%BA%E5%A4%9A%E4%B8%AA%E5%B1%9E%E6%80%A7%E7%9A%84%E7%AE%80%E5%8D%95%E6%96%B9%E5%BC%8F"><span class="nav-text">5. 创建多个属性的简单方式</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#5-1-sysfs-create-group"><span class="nav-text">5.1 sysfs_create_group()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-2-demo%E6%BA%90%E7%A0%81"><span class="nav-text">5.2 demo源码</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#5-3-%E5%BC%80%E5%8F%91%E6%9D%BF%E9%AA%8C%E8%AF%81"><span class="nav-text">5.3 开发板验证</span></a></li></ol></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="苏木"
      src="/images/avatar.jpg">
  <p class="site-author-name" itemprop="name">苏木</p>
  <div class="site-description" itemprop="description">莫道桑榆晚，为霞尚满天</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">673</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">42</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">43</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author animated">
      <span class="links-of-author-item">
        <a href="https://github.com/sumumm" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;sumumm" rel="noopener me" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
  </div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://sumumm.github.io/post/3cca4963.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.jpg">
      <meta itemprop="name" content="苏木">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="苏木">
      <meta itemprop="description" content="莫道桑榆晚，为霞尚满天">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="LV06-04-linux设备模型-05-属性文件 | 苏木">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          LV06-04-linux设备模型-05-属性文件
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2025-01-08 22:22:56" itemprop="dateCreated datePublished" datetime="2025-01-08T22:22:56+08:00">2025-01-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/" itemprop="url" rel="index"><span itemprop="name">嵌入式开发</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/" itemprop="url" rel="index"><span itemprop="name">02IMX6ULL平台</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/" itemprop="url" rel="index"><span itemprop="name">LV06-驱动开发</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>12k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>43 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody"><p>属性文件是什么？有什么用？怎么用？若笔记中有错误或者不合适的地方，欢迎批评指正😃。</p>
<span id="more"></span>

<!-- Photo: https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/ -->

<details class="folding-tag" blue><summary> 点击查看使用工具及版本 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center" rowspan="5">PC端开发环境</td>        <td align="center" width=150px>Windows</td>        <td align="left">Windows11</td>    </tr>    <tr>        <td align="center">Ubuntu</td>        <td align="left">Ubuntu20.04.2的64位版本</td>      </tr>    <tr>        <td align="center">VMware® Workstation 17 Pro</td>        <td align="left">17.6.0 build-24238078</td>      </tr>    <tr>        <td align="center">终端软件</td>        <td align="left">MobaXterm(Professional Edition v23.0 Build 5042 (license))</td>    </tr>    <tr>        <td align="center">Win32DiskImager</td>        <td align="left">Win32DiskImager v1.0</td>      </tr>    <tr>        <td align="center" rowspan="3">Linux开发板环境</td>        <td align="center">Linux开发板</td>        <td align="left">正点原子 i.MX6ULL Linux 阿尔法开发板</td>      </tr>    <tr>        <td align="center">uboot</td>        <td align="left">NXP官方提供的uboot，使用的uboot版本为U-Boot 2019.04</td>      </tr>    <tr>        <td align="center">linux内核</td>        <td align="left">linux-4.19.71(NXP官方提供)</td>      </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看本文参考资料 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center">分类</td>        <td align="center">网址</td>        <td align="center">说明</td>    </tr>    <tr>        <td align="center" rowspan="5">官方网站</td>        <td align="left"><a href="https://www.arm.com/" target="_blank">https://www.arm.com/</a></td>        <td align="left">ARM官方网站，在这里我们可以找到Cotex-Mx以及ARMVx的一些文档</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxp.com.cn/" target="_blank">https://www.nxp.com.cn/ </a></td>        <td align="left">NXP官方网站</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxpic.org.cn/" target="_blank">https://www.nxpic.org.cn/</a></td><td align="left">NXP 官方社区</td>    </tr>    <tr>        <td align="left"><a href="https://u-boot.readthedocs.io/en/latest/" target="_blank">https://u-boot.readthedocs.io/en/latest/</a></td><td align="left">u-boot官网</td>    </tr>    <tr>        <td align="left"><a href="https://www.kernel.org/" target="_blank">https://www.kernel.org/</a></td><td align="left">linux内核官网</td>    </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看相关文件下载 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center">分类</td>        <td align="center">网址</td>        <td align="center">说明</td>    </tr>    <tr>        <td align="center" rowspan="3">NXP</td>        <td align="left"><a href="https://github.com/nxp-imx" target="_blank">https://github.com/nxp-imx</a></td>        <td align="left">NXP imx开发资源GitHub组织，里边会有u-boot和linux内核的仓库</td>    </tr>    <tr>        <td align="left"><a href="https://github.com/nxp-imx/linux-imx/releases/tag/v4.19.71" target="_blank">nxp-imx/linux-imx/releases/tag/v4.19.71</a></td>        <td align="left">NXP linux内核仓库tags中的v4.19.71</td>    </tr>    <tr>        <td align="left"><a href="https://github.com/nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0" target="_blank">nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0</a></td>        <td align="left">NXP u-boot仓库tags中的rel_imx_4.19.35_1.1.0</td>    </tr>    <tr>        <td align="center" rowspan="2">I.MX6ULL</td>        <td align="left"><a href="https://www.nxp.com.cn/docs/en/data-sheet/IMX6ULLIEC.pdf" target="_blank">i.MX 6ULL Applications Processors for Industrial Products</a></td>        <td align="left">I.MX6ULL 芯片手册（datasheet，可以在线查看）</td>    </tr>    <tr>        <td align="left"><a href="https://www.nxp.com.cn/webapp/Download?colCode=IMX6ULLRM&lang_cd=zh" target="_blank">i.MX 6ULL Applications ProcessorReference Manual</a></td>        <td align="left">I.MX6ULL 参考手册（下载后才能查看，需要登录NXP官网）</td>    </tr>    <tr>        <td align="center" rowspan="3">Source Code</td>        <td align="left"><a href="https://elixir.bootlin.com/linux/latest/source" target="_blank">https://elixir.bootlin.com/linux/latest/source</a></td>        <td align="left">linux kernel源码</td>    </tr>    <tr>        <td align="left"><a href="https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/?h=v4.19.71&id=e7d2672c66e4d3675570369bf20856296da312c4" target="_blank">kernel/git/stable/linux.git - Linux kernel stable tree</a></td>        <td align="left">linux kernel源码(官网,tag 4.19.71)</td>    </tr>    <tr>        <td align="left"><a href="https://elixir.bootlin.com/u-boot/latest/source" target="_blank">https://elixir.bootlin.com/u-boot/latest/source</a></td>        <td align="left">uboot源码</td>    </tr></table>
              </div>
            </details>

<h1 id="一、属性文件是什么？"><a href="#一、属性文件是什么？" class="headerlink" title="一、属性文件是什么？"></a><font size=3>一、属性文件是什么？</font></h1><h2 id="1-属性文件简介"><a href="#1-属性文件简介" class="headerlink" title="1. 属性文件简介"></a><font size=3>1. 属性文件简介</font></h2><p>属性（attribute）文件就是就是内核空间和用户空间进行信息交互的一种方法。它是对应 kobject 而言的，指的是kobject的“属性”。</p>
<p>我们知道每一个注册的 kobject 都会在 &#x2F;sys 中以目录的形式呈现，也就是 bus 等数据结构可以利用嵌入 kobject 可以使它显示在 &#x2F;sys 中。而 attribute 又会以文件的形式出现在目录中, 即kobject的所有属性，都在它对应的sysfs目录下以文件的形式呈现。通过这些属性文件, 我们就能够获取&#x2F;修改内核中的变量，字符串等信息。</p>
<p>例如某个 driver 中定义了一个变量，希望用户空间程序可以修改该变量，以控制 driver 的运行行为，那么就可以将该变量以 sysfs attribute 的形式开放出来。</p>
<blockquote>
<p>Linux内核中，attribute分为普通的attribute和二进制attribute。</p>
</blockquote>
<h2 id="2-数据结构"><a href="#2-数据结构" class="headerlink" title="2. 数据结构"></a><font size=3>2. 数据结构</font></h2><p>会接触到的属性相关的结构体大概有以下几个。</p>
<h3 id="2-1-struct-attribute"><a href="#2-1-struct-attribute" class="headerlink" title="2.1 struct attribute"></a><font size=3>2.1 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L30">struct attribute</a></font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L30">struct attribute</a> 定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> &#123;</span></span><br><span class="line">	<span class="type">const</span> <span class="type">char</span>		*name;</span><br><span class="line">	<span class="type">umode_t</span>			mode;</span><br><span class="line"><span class="meta">#<span class="keyword">ifdef</span> CONFIG_DEBUG_LOCK_ALLOC</span></span><br><span class="line">	<span class="type">bool</span>			ignore_lockdep:<span class="number">1</span>;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">lock_class_key</span>	*<span class="title">key</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">lock_class_key</span>	<span class="title">skey</span>;</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>其中 name 表示文件名称，mode表示文件模式（也就是权限，比如 0644、0666 等）。其他几个是调试用的。</p>
<h3 id="2-2-struct-bin-attribute"><a href="#2-2-struct-bin-attribute" class="headerlink" title="2.2 struct bin_attribute"></a><font size=3>2.2 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L161">struct bin_attribute</a></font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L161">struct bin_attribute</a> 定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">bin_attribute</span> &#123;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">attribute</span>	<span class="title">attr</span>;</span></span><br><span class="line">	<span class="type">size_t</span>			size;</span><br><span class="line">	<span class="type">void</span>			*private;</span><br><span class="line">	<span class="type">ssize_t</span> (*read)(<span class="keyword">struct</span> file *, <span class="keyword">struct</span> kobject *, <span class="keyword">struct</span> bin_attribute *,</span><br><span class="line">			<span class="type">char</span> *, <span class="type">loff_t</span>, <span class="type">size_t</span>);</span><br><span class="line">	<span class="type">ssize_t</span> (*write)(<span class="keyword">struct</span> file *, <span class="keyword">struct</span> kobject *, <span class="keyword">struct</span> bin_attribute *,</span><br><span class="line">			 <span class="type">char</span> *, <span class="type">loff_t</span>, <span class="type">size_t</span>);</span><br><span class="line">	<span class="type">int</span> (*mmap)(<span class="keyword">struct</span> file *, <span class="keyword">struct</span> kobject *, <span class="keyword">struct</span> bin_attribute *attr,</span><br><span class="line">		    <span class="keyword">struct</span> vm_area_struct *vma);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h3 id="2-3-struct-attribute-group"><a href="#2-3-struct-attribute-group" class="headerlink" title="2.3 struct attribute_group"></a><font size=3>2.3 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L84">struct attribute_group</a></font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L84">struct attribute_group</a> 定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute_group</span> &#123;</span></span><br><span class="line">	<span class="type">const</span> <span class="type">char</span>		*name;</span><br><span class="line">	<span class="type">umode_t</span>			(*is_visible)(<span class="keyword">struct</span> kobject *,</span><br><span class="line">					      <span class="keyword">struct</span> attribute *, <span class="type">int</span>);</span><br><span class="line">	<span class="type">umode_t</span>			(*is_bin_visible)(<span class="keyword">struct</span> kobject *,</span><br><span class="line">						  <span class="keyword">struct</span> bin_attribute *, <span class="type">int</span>);</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">attribute</span>	**<span class="title">attrs</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">bin_attribute</span>	**<span class="title">bin_attrs</span>;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h3 id="2-4-struct-kobj-attribute"><a href="#2-4-struct-kobj-attribute" class="headerlink" title="2.4 struct kobj_attribute"></a><font size=3>2.4 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/kobject.h#L163">struct kobj_attribute</a></font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/kobject.h#L163">struct kobj_attribute</a> 定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> &#123;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> <span class="title">attr</span>;</span></span><br><span class="line">	<span class="type">ssize_t</span> (*show)(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr,</span><br><span class="line">			<span class="type">char</span> *buf);</span><br><span class="line">	<span class="type">ssize_t</span> (*store)(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr,</span><br><span class="line">			 <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>这个结构体包含一个attr成员，它就是前面的属性结构体，后面的show和store函数指针就是为每个属性指定不同的回调函数的。</p>
<h2 id="3-相关API"><a href="#3-相关API" class="headerlink" title="3. 相关API"></a><font size=3>3. 相关API</font></h2><h3 id="3-1-sysfs-create-file"><a href="#3-1-sysfs-create-file" class="headerlink" title="3.1 sysfs_create_file()"></a><font size=3>3.1 sysfs_create_file()</font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L510">sysfs_create_file()</a> 函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">int</span> __must_check <span class="title function_">sysfs_create_file</span><span class="params">(<span class="keyword">struct</span> kobject *kobj,</span></span><br><span class="line"><span class="params">						 <span class="type">const</span> <span class="keyword">struct</span> attribute *attr)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">return</span> sysfs_create_file_ns(kobj, attr, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="3-2-sysfs-create-files"><a href="#3-2-sysfs-create-files" class="headerlink" title="3.2 sysfs_create_files()"></a><font size=3>3.2 sysfs_create_files()</font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L337">sysfs_create_files()</a> 函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">sysfs_create_files</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="type">const</span> <span class="keyword">struct</span> attribute **ptr)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> err = <span class="number">0</span>;</span><br><span class="line">	<span class="type">int</span> i;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> (i = <span class="number">0</span>; ptr[i] &amp;&amp; !err; i++)</span><br><span class="line">		err = sysfs_create_file(kobj, ptr[i]);</span><br><span class="line">	<span class="keyword">if</span> (err)</span><br><span class="line">		<span class="keyword">while</span> (--i &gt;= <span class="number">0</span>)</span><br><span class="line">			sysfs_remove_file(kobj, ptr[i]);</span><br><span class="line">	<span class="keyword">return</span> err;</span><br><span class="line">&#125;</span><br><span class="line">EXPORT_SYMBOL_GPL(sysfs_create_files);</span><br></pre></td></tr></table></figure>

<h3 id="3-3-sysfs-remove-file"><a href="#3-3-sysfs-remove-file" class="headerlink" title="3.3 sysfs_remove_file()"></a><font size=3>3.3 sysfs_remove_file()</font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L516">sysfs_remove_file()</a> 函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="keyword">inline</span> <span class="type">void</span> <span class="title function_">sysfs_remove_file</span><span class="params">(<span class="keyword">struct</span> kobject *kobj,</span></span><br><span class="line"><span class="params">				     <span class="type">const</span> <span class="keyword">struct</span> attribute *attr)</span></span><br><span class="line">&#123;</span><br><span class="line">	sysfs_remove_file_ns(kobj, attr, <span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="3-4-sysfs-remove-files"><a href="#3-4-sysfs-remove-files" class="headerlink" title="3.4 sysfs_remove_files()"></a><font size=3>3.4 sysfs_remove_files()</font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L496">sysfs_remove_files()</a> 函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">sysfs_create_files</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="type">const</span> <span class="keyword">struct</span> attribute **ptr)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">int</span> err = <span class="number">0</span>;</span><br><span class="line">	<span class="type">int</span> i;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">for</span> (i = <span class="number">0</span>; ptr[i] &amp;&amp; !err; i++)</span><br><span class="line">		err = sysfs_create_file(kobj, ptr[i]);</span><br><span class="line">	<span class="keyword">if</span> (err)</span><br><span class="line">		<span class="keyword">while</span> (--i &gt;= <span class="number">0</span>)</span><br><span class="line">			sysfs_remove_file(kobj, ptr[i]);</span><br><span class="line">	<span class="keyword">return</span> err;</span><br><span class="line">&#125;</span><br><span class="line">EXPORT_SYMBOL_GPL(sysfs_create_files);</span><br></pre></td></tr></table></figure>

<h3 id="3-5-sysfs-create-group"><a href="#3-5-sysfs-create-group" class="headerlink" title="3.5 sysfs_create_group()"></a><font size=3>3.5 sysfs_create_group()</font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/group.c#L160">sysfs_create_group()</a>函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">sysfs_create_group</span><span class="params">(<span class="keyword">struct</span> kobject *kobj,</span></span><br><span class="line"><span class="params">		       <span class="type">const</span> <span class="keyword">struct</span> attribute_group *grp)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="keyword">return</span> internal_create_group(kobj, <span class="number">0</span>, grp);</span><br><span class="line">&#125;</span><br><span class="line">EXPORT_SYMBOL_GPL(sysfs_create_group);</span><br></pre></td></tr></table></figure>

<p>用于在 sysfs 中创建一个组(group)。组是一组相关的属性文件的集合，可以将它们放在同一个目录下提供更好的组织性和可读性。此函数有俩个参数，分别为如下所示：</p>
<ul>
<li>kobj: 指向包含目标组的 kobject 的指针。 </li>
<li>grp: 指向 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L61">struct attribute_group</a> 结构体的指针，该结构体定义了组中的属性文件。</li>
</ul>
<p> <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L61">struct attribute_group</a> 定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute_group</span> &#123;</span></span><br><span class="line">    <span class="comment">// 组的名称，将用作 sysfs 目录的名称。</span></span><br><span class="line">	<span class="type">const</span> <span class="type">char</span>		*name;</span><br><span class="line">    <span class="comment">// 可选的回调函数，用于决定每个属性文件是否可见。如果为 NULL，则所有属性文件都可见。</span></span><br><span class="line">	<span class="type">umode_t</span>			(*is_visible)(<span class="keyword">struct</span> kobject *,</span><br><span class="line">					      <span class="keyword">struct</span> attribute *, <span class="type">int</span>);</span><br><span class="line">	<span class="type">umode_t</span>			(*is_bin_visible)(<span class="keyword">struct</span> kobject *,</span><br><span class="line">						  <span class="keyword">struct</span> bin_attribute *, <span class="type">int</span>);</span><br><span class="line">    <span class="comment">// 指向属性文件数组的指针</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">attribute</span>	**<span class="title">attrs</span>;</span> </span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">bin_attribute</span>	**<span class="title">bin_attrs</span>;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h3 id="3-6-ATTR"><a href="#3-6-ATTR" class="headerlink" title="3.6 __ATTR()"></a><font size=3>3.6 __ATTR()</font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L101">__ATTR()</a> 是一个宏，定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">define</span> __ATTR(_name, _mode, _show, _store) &#123;				\</span></span><br><span class="line"><span class="meta">	.attr = &#123;.name = __stringify(_name),				\</span></span><br><span class="line"><span class="meta">		 .mode = VERIFY_OCTAL_PERMISSIONS(_mode) &#125;,		\</span></span><br><span class="line"><span class="meta">	.show	= _show,						\</span></span><br><span class="line"><span class="meta">	.store	= _store,						\</span></span><br><span class="line"><span class="meta">&#125;</span></span><br></pre></td></tr></table></figure>

<p>该宏可以用于简化属性文件（attribute file）的定义。它用于定义一个 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L30">struct attribute</a> 结构体，同时设置该属性的名称、权限以及关联的 <code>show</code> 和 <code>store</code> 操作函数。</p>
<ul>
<li>_name：属性的名称，将出现在 &#x2F;sys 文件系统中的文件名，这个名称可以不带<code>&quot; &quot;</code>，里面会自己处理成字符串。</li>
<li>_mode：属性文件的权限模式，通常是八进制数，比如 0644、0666 等，表示文件的读写权限。</li>
<li>_show：属性文件的读取函数指针，当用户读取该文件时将调用此函数。</li>
<li>_store：属性文件的写入函数指针，当用户写入该文件时将调用此函数。</li>
</ul>
<h2 id="4-属性文件的典型用例"><a href="#4-属性文件的典型用例" class="headerlink" title="4. 属性文件的典型用例"></a><font size=3>4. 属性文件的典型用例</font></h2><p>一个典型的例子是 &#x2F;sys&#x2F;class&#x2F;net&#x2F;eth0&#x2F; 目录下的属性文件，它们允许用户查看或配置网络接口 eth0 的一些属性，比如 speed、mtu、operstate 等。</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107095722933.png" alt="image-20250107095722933"  />

<p>我们可以用以下命令读取相关信息：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cat /sys/class/net/eth0/speed       # 读取速度</span><br><span class="line">echo 1500 &gt; /sys/class/net/eth0/mtu # 配置 MTU</span><br></pre></td></tr></table></figure>

<h1 id="二、属性文件的创建与调用逻辑"><a href="#二、属性文件的创建与调用逻辑" class="headerlink" title="二、属性文件的创建与调用逻辑"></a><font size=3>二、属性文件的创建与调用逻辑</font></h1><h2 id="1-sysfs-create-file"><a href="#1-sysfs-create-file" class="headerlink" title="1. sysfs_create_file()"></a><font size=3>1. sysfs_create_file()</font></h2><p>接下来看一下属性文件怎么被创建的，我们从 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L510">sysfs_create_file()</a> 函数入手，从这个函数入手，看一下是怎么个逻辑：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107104840696.png" alt="image-20250107104840696"  />

<p>可以看到，我们创建kobject的时候，会调用到 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L510">sysfs_create_file()</a> 函数来创建属性文件。</p>
<h3 id="1-1-sysfs-create-file-ns"><a href="#1-1-sysfs-create-file-ns" class="headerlink" title="1.1 sysfs_create_file_ns()"></a><font size=3>1.1 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L316">sysfs_create_file_ns()</a></font></h3><p> <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L510">sysfs_create_file()</a> 函数内部调用了<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L316">sysfs_create_file_ns()</a>，我们看一下<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L316">sysfs_create_file_ns()</a>函数：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">sysfs_create_file_ns</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="type">const</span> <span class="keyword">struct</span> attribute *attr,</span></span><br><span class="line"><span class="params">			 <span class="type">const</span> <span class="type">void</span> *ns)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="type">kuid_t</span> uid;</span><br><span class="line">	<span class="type">kgid_t</span> gid;</span><br><span class="line"></span><br><span class="line">	BUG_ON(!kobj || !kobj-&gt;sd || !attr);</span><br><span class="line"></span><br><span class="line">	kobject_get_ownership(kobj, &amp;uid, &amp;gid);</span><br><span class="line">	<span class="keyword">return</span> sysfs_add_file_mode_ns(kobj-&gt;sd, attr, <span class="literal">false</span>, attr-&gt;mode,</span><br><span class="line">				      uid, gid, ns);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">EXPORT_SYMBOL_GPL(sysfs_create_file_ns);</span><br></pre></td></tr></table></figure>

<p>这里一共调用了两个函数，我们主要关注这个 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L246">sysfs_add_file_mode_ns()</a> 函数。</p>
<h3 id="1-2-sysfs-add-file-mode-ns"><a href="#1-2-sysfs-add-file-mode-ns" class="headerlink" title="1.2 sysfs_add_file_mode_ns()"></a><font size=3>1.2 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L246">sysfs_add_file_mode_ns()</a></font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L246">sysfs_add_file_mode_ns()</a> 函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">sysfs_add_file_mode_ns</span><span class="params">(<span class="keyword">struct</span> kernfs_node *parent,</span></span><br><span class="line"><span class="params">			   <span class="type">const</span> <span class="keyword">struct</span> attribute *attr, <span class="type">bool</span> is_bin,</span></span><br><span class="line"><span class="params">			   <span class="type">umode_t</span> mode, <span class="type">kuid_t</span> uid, <span class="type">kgid_t</span> gid, <span class="type">const</span> <span class="type">void</span> *ns)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">lock_class_key</span> *<span class="title">key</span> =</span> <span class="literal">NULL</span>;</span><br><span class="line">	<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">kernfs_ops</span> *<span class="title">ops</span>;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">kernfs_node</span> *<span class="title">kn</span>;</span></span><br><span class="line">	<span class="type">loff_t</span> size;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (!is_bin) &#123;</span><br><span class="line">		<span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> *<span class="title">kobj</span> =</span> parent-&gt;priv;</span><br><span class="line">		<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">sysfs_ops</span> *<span class="title">sysfs_ops</span> =</span> kobj-&gt;ktype-&gt;sysfs_ops;</span><br><span class="line"></span><br><span class="line">		<span class="comment">/* every kobject with an attribute needs a ktype assigned */</span></span><br><span class="line">		<span class="keyword">if</span> (WARN(!sysfs_ops, KERN_ERR</span><br><span class="line">			 <span class="string">&quot;missing sysfs attribute operations for kobject: %s\n&quot;</span>,</span><br><span class="line">			 kobject_name(kobj)))</span><br><span class="line">			<span class="keyword">return</span> -EINVAL;</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> (sysfs_ops-&gt;show &amp;&amp; sysfs_ops-&gt;store) &#123;</span><br><span class="line">			<span class="keyword">if</span> (mode &amp; SYSFS_PREALLOC)</span><br><span class="line">				ops = &amp;sysfs_prealloc_kfops_rw;</span><br><span class="line">			<span class="keyword">else</span></span><br><span class="line">				ops = &amp;sysfs_file_kfops_rw;</span><br><span class="line">		&#125; <span class="keyword">else</span> <span class="keyword">if</span> (sysfs_ops-&gt;show) &#123;</span><br><span class="line">			<span class="comment">//......</span></span><br><span class="line">		&#125; <span class="keyword">else</span> <span class="keyword">if</span> (sysfs_ops-&gt;store) &#123;</span><br><span class="line">			<span class="comment">// ......</span></span><br><span class="line">		&#125; <span class="keyword">else</span></span><br><span class="line">			ops = &amp;sysfs_file_kfops_empty;</span><br><span class="line"></span><br><span class="line">		size = PAGE_SIZE;</span><br><span class="line">	&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">		<span class="comment">//......</span></span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里大概得逻辑就是从 kobj-&gt;ktype 获取 sysfs_ops ，这个成员是 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L214">struct sysfs_ops</a>类型，它的定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sysfs_ops</span> &#123;</span></span><br><span class="line">	<span class="type">ssize_t</span>	(*show)(<span class="keyword">struct</span> kobject *, <span class="keyword">struct</span> attribute *, <span class="type">char</span> *);</span><br><span class="line">	<span class="type">ssize_t</span>	(*store)(<span class="keyword">struct</span> kobject *, <span class="keyword">struct</span> attribute *, <span class="type">const</span> <span class="type">char</span> *, <span class="type">size_t</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>从后面的一堆if中，可以知道，当我们定义了这两个函数的时候，ops指针就会指向 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L221">sysfs_prealloc_kfops_rw</a> 或者 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L206">sysfs_file_kfops_rw</a>，具体指向哪一个，受到mode的影响，这个mode其实就是创建kobject的时候的属性的mode成员，表示读写权限，我们一般想要读写的话，这里会赋值为 0664，这是一个8进制数，<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L99">SYSFS_PREALLOC</a> 为010000，这也是八进制，所以这里&amp;运算是不成立的，所以最后指向的是 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L206">sysfs_file_kfops_rw</a>，这个变量定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">kernfs_ops</span> <span class="title">sysfs_file_kfops_rw</span> =</span> &#123;</span><br><span class="line">	.seq_show	= sysfs_kf_seq_show,</span><br><span class="line">	.write		= sysfs_kf_write,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>这里后面的逻辑我就没有去详细分析了，但是大概就是，当对属性文件进行读写的时候就会去调用 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L206">sysfs_file_kfops_rw</a> 中的这两个函数，这里相当于是告诉读写的时候要用哪两个函数，就像驱动注册的时候我们会提供操作函数集一样。</p>
<h3 id="1-3-总结"><a href="#1-3-总结" class="headerlink" title="1.3 总结"></a><font size=3>1.3 总结</font></h3><p>上面大概就是属性文件的创建过程，其实并没有分析的很完整，但是其实也没啥必要，大概知道个流程就行了，再分析就涉及很多东西了，这里就不花费时间去分析了。但是足够我们后面去找属性文件的读写逻辑了。</p>
<h2 id="2-属性文件的读写逻辑"><a href="#2-属性文件的读写逻辑" class="headerlink" title="2. 属性文件的读写逻辑"></a><font size=3>2. 属性文件的读写逻辑</font></h2><p>前面呢，分析了一堆，大概可以知道，创建文件的时候，为这个文件的操作注册了对应的读写函数，就是 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L206">sysfs_file_kfops_rw</a> 这个变量中的两个函数。但是最终是怎么操作的？我们以读的函数为例来看一下。</p>
<h3 id="2-1-sysfs-kf-seq-show"><a href="#2-1-sysfs-kf-seq-show" class="headerlink" title="2.1 sysfs_kf_seq_show()"></a><font size=3>2.1 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L40">sysfs_kf_seq_show()</a></font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L40">sysfs_kf_seq_show()</a> 函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">sysfs_kf_seq_show</span><span class="params">(<span class="keyword">struct</span> seq_file *sf, <span class="type">void</span> *v)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">kernfs_open_file</span> *<span class="title">of</span> =</span> sf-&gt;private;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> *<span class="title">kobj</span> =</span> of-&gt;kn-&gt;parent-&gt;priv;</span><br><span class="line">	<span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">sysfs_ops</span> *<span class="title">ops</span> =</span> sysfs_file_ops(of-&gt;kn);</span><br><span class="line">	<span class="type">ssize_t</span> count;</span><br><span class="line">	<span class="type">char</span> *buf;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="keyword">if</span> (ops-&gt;show) &#123;</span><br><span class="line">		count = ops-&gt;show(kobj, of-&gt;kn-&gt;priv, buf);</span><br><span class="line">		<span class="keyword">if</span> (count &lt; <span class="number">0</span>)</span><br><span class="line">			<span class="keyword">return</span> count;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">//......</span></span><br><span class="line">	<span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里可以看到定义了一个 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L214">struct sysfs_ops</a> 类型指针，指向了 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L26">sysfs_file_ops()</a> 的返回值，最后调用了其中的show函数，具体这个私有信息是怎么来的，应该文件系统那边去做的这里暂时就不详细去分析了。我们目标只有一个，就是找到这个show函数。</p>
<h3 id="2-2-sysfs-file-ops"><a href="#2-2-sysfs-file-ops" class="headerlink" title="2.2 sysfs_file_ops()"></a><font size=3>2.2 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L26">sysfs_file_ops()</a></font></h3><p><a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L26">sysfs_file_ops()</a> 函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">const</span> <span class="keyword">struct</span> sysfs_ops *<span class="title function_">sysfs_file_ops</span><span class="params">(<span class="keyword">struct</span> kernfs_node *kn)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> *<span class="title">kobj</span> =</span> kn-&gt;parent-&gt;priv;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (kn-&gt;flags &amp; KERNFS_LOCKDEP)</span><br><span class="line">		lockdep_assert_held(kn);</span><br><span class="line">	<span class="keyword">return</span> kobj-&gt;ktype ? kobj-&gt;ktype-&gt;sysfs_ops : <span class="literal">NULL</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>到这里其实大概就比较清楚了，<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L26">sysfs_file_ops()</a> 函数返回的其实就是ktype中的 sysfs_ops 操作函数集。</p>
<h3 id="2-3-总结"><a href="#2-3-总结" class="headerlink" title="2.3 总结"></a><font size=3>2.3 总结</font></h3><p>这里不再深入去分析了，内核太庞大了，以后有机会再说。到这里，我们大概知道了属性文件大概的调用逻辑。处理过程大概就是：</p>
<p>在 cat&#x2F;echo 属性文件时(读&#x2F;写属性文件写数据时)</p>
<ul>
<li>（1）先调用 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L26">sysfs_file_ops()</a>  获取到<code>kobj-&gt;ktype-&gt;sysfs_ops 指针</code></li>
<li>（2）调用对应内核的 show&#x2F;store 函数。</li>
</ul>
<p>从这里可以得出两条结论:</p>
<ul>
<li><p>对于用户空间来讲，只负责把数据丢给内核的 store 以及从内核的 show 函数获取数据，至于 store 的数据用来做什么和 show 获取到数据表示什么意思则由内核决定。</p>
</li>
<li><p>如果从属的 kobject（就是 attribute 文件所在的目录）没有 ktype，或者没有 ktype-&gt;sysfs_ops 指针，是不允许它注册任何 attribute 的。</p>
</li>
</ul>
<h1 id="三、属性文件的读写实现与优化"><a href="#三、属性文件的读写实现与优化" class="headerlink" title="三、属性文件的读写实现与优化"></a><font size=3>三、属性文件的读写实现与优化</font></h1><h2 id="1-属性文件创建demo"><a href="#1-属性文件创建demo" class="headerlink" title="1. 属性文件创建demo"></a><font size=3>1. 属性文件创建demo</font></h2><h3 id="1-1-demo源码"><a href="#1-1-demo源码" class="headerlink" title="1.1 demo源码"></a><font size=3>1.1 demo源码</font></h3><p>我们实现这样一个功能，自定义一个kobject：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> __<span class="title">SKOBJ_</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> skobj_name[<span class="number">32</span>];</span><br><span class="line">    <span class="type">char</span> name_attr[<span class="number">32</span>];</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> <span class="title">kobj</span>;</span></span><br><span class="line">&#125;<span class="type">skobj_t</span>;</span><br></pre></td></tr></table></figure>

<p>创建两个属性文件，对应name_attr和data，可以通过cat命令和echo命令修改和显示这两个的值。demo可以看这里：<a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/tree/master/05_device_model/06_attr_file_demo1">05_device_model&#x2F;06_attr_file_demo1 · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a></p>
<details class="folding-tag" blue><summary> 点击查看详情 </summary>
              <div class='content'>
              <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/init.h&gt;</span> <span class="comment">/* module_init module_exit */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kernel.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/module.h&gt;</span> <span class="comment">/* MODULE_LICENSE */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kobject.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/slab.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./timestamp_autogenerated.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./version_autogenerated.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./sdrv_common.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// #undef PRT</span></span><br><span class="line"><span class="comment">// #undef PRTE</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PRT</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRT printk</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PRTE</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRTE printk</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> __<span class="title">SKOBJ_</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> skobj_name[<span class="number">32</span>];</span><br><span class="line">    <span class="type">char</span> name_attr[<span class="number">32</span>];</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> <span class="title">kobj</span>;</span></span><br><span class="line">&#125;<span class="type">skobj_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">skobj_t</span> *skobject1 = <span class="literal">NULL</span>; <span class="comment">// 定义kobject指针变量：skobject1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  dynamic_kobj_release()</span></span><br><span class="line"><span class="comment"> * @note   定义kobject的释放函数</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">dynamic_kobj_release</span><span class="params">(<span class="keyword">struct</span> kobject *kobj)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line">    PRT(<span class="string">&quot;kobject: (%p) \n&quot;</span>, kobj);</span><br><span class="line">    kfree(pskobj);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_show()</span></span><br><span class="line"><span class="comment"> * @note   读属性操作函数, cat 属性文件时调用这个函数，会把对应的值显示到终端</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> attribute *attr, <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">ssize_t</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">strcmp</span>(attr-&gt;name, <span class="string">&quot;skobj_attr_name&quot;</span>) == <span class="number">0</span>) <span class="comment">// skobj_attr_name 属性对应 pskobj-&gt;name 成员</span></span><br><span class="line">    &#123;</span><br><span class="line">        count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, pskobj-&gt;name_attr);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(<span class="built_in">strcmp</span>(attr-&gt;name, <span class="string">&quot;skobj_attr_data&quot;</span>) == <span class="number">0</span>) <span class="comment">// skobj_attr_data 属性对应 pskobj-&gt;data 成员</span></span><br><span class="line">    &#123;</span><br><span class="line">        count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%d\n&quot;</span>, pskobj-&gt;data);</span><br><span class="line">    &#125;</span><br><span class="line">    PRT(<span class="string">&quot;attr-&gt;name=%s count=%d\n&quot;</span>, attr-&gt;name, count);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_store()</span></span><br><span class="line"><span class="comment"> * @note   写属性操作文件, echo 属性文件时调用这个函数，用于echo x 中的 x 写入到属性对应的成员变量中</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_store</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> attribute *attr, <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line"></span><br><span class="line">    PRT(<span class="string">&quot;attr-&gt;name=%s count=%d\n&quot;</span>, attr-&gt;name, count);</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">strcmp</span>(attr-&gt;name, <span class="string">&quot;skobj_attr_name&quot;</span>) == <span class="number">0</span>)      <span class="comment">// skobj_attr_name 属性对应 pskobj-&gt;name 成员</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sscanf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, pskobj-&gt;name_attr);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(<span class="built_in">strcmp</span>(attr-&gt;name, <span class="string">&quot;skobj_attr_data&quot;</span>) == <span class="number">0</span>) <span class="comment">// skobj_attr_data 属性对应 pskobj-&gt;data 成员</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sscanf</span>(buf, <span class="string">&quot;%d\n&quot;</span>, &amp;pskobj-&gt;data);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义的 sysfs_ops 结构体，包含 show 和 store 函数指</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sysfs_ops</span> <span class="title">attr_sysfs_ops</span> =</span> &#123;</span><br><span class="line">    .show  = skobj_attr_show,</span><br><span class="line">    .store = skobj_attr_store,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义的 kobj_type 结构体，包含释放函数、默认属性和 sysfs_ops</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_type</span> <span class="title">stype</span> =</span> &#123;</span><br><span class="line">    .release = dynamic_kobj_release,</span><br><span class="line">    .sysfs_ops = &amp;attr_sysfs_ops,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 每一个注册的 attribute 结构都是一个属性文件，这里创建两个属性文件</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> <span class="title">s_attr_name</span> =</span> &#123;</span><br><span class="line">    .name = <span class="string">&quot;skobj_attr_name&quot;</span>,</span><br><span class="line">    .mode = S_IRWXUGO,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> <span class="title">s_attr_data</span> =</span> &#123;</span><br><span class="line">    .name = <span class="string">&quot;skobj_attr_data&quot;</span>,</span><br><span class="line">    .mode = S_IRWXUGO,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  sdriver_demo_init()</span></span><br><span class="line"><span class="comment"> * @note   </span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> __init <span class="type">int</span> <span class="title function_">sdriver_demo_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">	printk(<span class="string">&quot;*** [%s:%d]Build Time: %s %s, git version:%s ***\n&quot;</span>, __FUNCTION__,</span><br><span class="line">           __LINE__, KERNEL_KO_DATE, KERNEL_KO_TIME, KERNEL_KO_VERSION);</span><br><span class="line">    PRT(<span class="string">&quot;sdriver_demo module init!\n&quot;</span>);</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建kobject的第二种方法</span></span><br><span class="line">    <span class="comment">// 1.使用kzalloc函数分配了一个kobject对象的内存</span></span><br><span class="line">    skobject1 = kzalloc(<span class="keyword">sizeof</span>(<span class="type">skobj_t</span>), GFP_KERNEL);</span><br><span class="line">    <span class="keyword">if</span>(!skobject1)</span><br><span class="line">    &#123;</span><br><span class="line">        ret = <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">goto</span> err_kzalloc;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">snprintf</span>(skobject1-&gt;skobj_name, <span class="keyword">sizeof</span>(skobject1-&gt;skobj_name), <span class="string">&quot;skobject1&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.初始化并添加到内核中，名为&quot;skobject1&quot;。</span></span><br><span class="line">    ret = kobject_init_and_add(&amp;skobject1-&gt;kobj, &amp;stype, <span class="literal">NULL</span>, <span class="string">&quot;%s&quot;</span>, skobject1-&gt;skobj_name);</span><br><span class="line">    <span class="keyword">if</span>(ret &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        PRTE(<span class="string">&quot;kobject_init_and_add fail!ret=%d\n&quot;</span>, ret);</span><br><span class="line">        <span class="keyword">goto</span> err_kobject_init_and_add;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//创建 s_attr_a 属性文件</span></span><br><span class="line">    ret = sysfs_create_file(&amp;skobject1-&gt;kobj, &amp;s_attr_name);</span><br><span class="line">    <span class="keyword">if</span>(ret &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        PRTE(<span class="string">&quot;sysfs_create_file s_attr_name fail!ret=%d\n&quot;</span>, ret);</span><br><span class="line">        <span class="keyword">goto</span> err_sysfs_create_file_1;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//创建 s_attr_b 属性文件</span></span><br><span class="line">    ret = sysfs_create_file(&amp;skobject1-&gt;kobj, &amp;s_attr_data);</span><br><span class="line">    <span class="keyword">if</span>(ret &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        PRTE(<span class="string">&quot;sysfs_create_file s_attr_data fail!ret=%d\n&quot;</span>, ret);</span><br><span class="line">        <span class="keyword">goto</span> err_sysfs_create_file_2;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">err_sysfs_create_file_2:</span><br><span class="line">    sysfs_remove_file(&amp;skobject1-&gt;kobj, &amp;s_attr_name);</span><br><span class="line">err_sysfs_create_file_1:</span><br><span class="line">    kobject_put(&amp;skobject1-&gt;kobj);</span><br><span class="line">err_kobject_init_and_add:</span><br><span class="line">    kfree(skobject1);</span><br><span class="line">err_kzalloc:</span><br><span class="line">	<span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  sdriver_demo_exit()</span></span><br><span class="line"><span class="comment"> * @note   </span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> __exit <span class="type">void</span> <span class="title function_">sdriver_demo_exit</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">// 删除创建的属性文件，好像不删除的话，在释放kobj的时候会自动删除？</span></span><br><span class="line">    sysfs_remove_file(&amp;skobject1-&gt;kobj, &amp;s_attr_name);</span><br><span class="line">    sysfs_remove_file(&amp;skobject1-&gt;kobj, &amp;s_attr_data);</span><br><span class="line">	<span class="comment">// 释放之前创建的kobject对象</span></span><br><span class="line">    kobject_put(&amp;skobject1-&gt;kobj);</span><br><span class="line">    PRT(<span class="string">&quot;sdriver_demo module exit!\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">module_init(sdriver_demo_init); <span class="comment">// 将__init定义的函数指定为驱动的入口函数</span></span><br><span class="line">module_exit(sdriver_demo_exit); <span class="comment">// 将__exit定义的函数指定为驱动的出口函数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 模块信息(通过 modinfo chrdev_led_demo 查看) */</span></span><br><span class="line">MODULE_LICENSE(<span class="string">&quot;GPL v2&quot;</span>);            <span class="comment">/* 源码的许可证协议 */</span></span><br><span class="line">MODULE_AUTHOR(<span class="string">&quot;sumu&quot;</span>);               <span class="comment">/* 字符串常量内容为模块作者说明 */</span></span><br><span class="line">MODULE_DESCRIPTION(<span class="string">&quot;Description&quot;</span>);   <span class="comment">/* 字符串常量内容为模块功能说明 */</span></span><br><span class="line">MODULE_ALIAS(<span class="string">&quot;module&#x27;s other name&quot;</span>); <span class="comment">/* 字符串常量内容为模块别名 */</span></span><br></pre></td></tr></table></figure>
              </div>
            </details>

<p>这个demo是并没有将属性写入到ktype中，所以不会自动创建对应的属性文件，需要我们自己调用 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/file.c#L337">sysfs_create_files()</a> 函数创建。</p>
<h3 id="1-2-开发板验证"><a href="#1-2-开发板验证" class="headerlink" title="1.2 开发板验证"></a><font size=3>1.2 开发板验证</font></h3><p>我们编译完，会得到一个 sdriver_demo.ko 文件。接下来开始验证我们写的demo。</p>
<ul>
<li>（1）一开始的 &#x2F;sys目录</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107164518539.png" alt="image-20250107164518539"  />

<ul>
<li>（2）加载模块</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107164601577.png" alt="image-20250107164601577"  />

<p>加载模块后，会发现，在&#x2F;sys目录生成了 skobject1 目录，在&#x2F;sys&#x2F;skobject1目录下有两个属性文件skobj_attr_name和skobj_attr_data。</p>
<ul>
<li>（3）查看skobj_attr_name和skobj_attr_data的值</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107164739343.png" alt="image-20250107164739343"  />

<ul>
<li>（4）修改skobj_attr_name和skobj_attr_data的值</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107165743466.png" alt="image-20250107165743466"  />

<ul>
<li>（5）卸载模块</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107165920554.png" alt="image-20250107165920554"  />

<p>可以看到，卸载模块后，资源释放，两个属性文件以及kobject目录全都没了。</p>
<h2 id="2-将属性放入ktype"><a href="#2-将属性放入ktype" class="headerlink" title="2. 将属性放入ktype"></a><font size=3>2. 将属性放入ktype</font></h2><p>其实kobject在创建的时候，是会自动帮我们创建属性的，应该是在 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/lib/kobject.c#L73">populate_dir()</a> 函数中：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="type">int</span> <span class="title function_">populate_dir</span><span class="params">(<span class="keyword">struct</span> kobject *kobj)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">kobj_type</span> *<span class="title">t</span> =</span> get_ktype(kobj);</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> *<span class="title">attr</span>;</span></span><br><span class="line">	<span class="type">int</span> error = <span class="number">0</span>;</span><br><span class="line">	<span class="type">int</span> i;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> (t &amp;&amp; t-&gt;default_attrs) &#123;</span><br><span class="line">		<span class="keyword">for</span> (i = <span class="number">0</span>; (attr = t-&gt;default_attrs[i]) != <span class="literal">NULL</span>; i++) &#123;</span><br><span class="line">			error = sysfs_create_file(kobj, attr);</span><br><span class="line">			<span class="keyword">if</span> (error)</span><br><span class="line">				<span class="keyword">break</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> error;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看到我们需要把属性给放到ktype的default_attrs成员中。</p>
<h3 id="2-1-demo源码"><a href="#2-1-demo源码" class="headerlink" title="2.1 demo源码"></a><font size=3>2.1 demo源码</font></h3><p><a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/tree/master/05_device_model/07_attr_file_demo2">05_device_model&#x2F;07_attr_file_demo2 · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a></p>
<details class="folding-tag" blue><summary> 点击查看详情 </summary>
              <div class='content'>
              <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/init.h&gt;</span> <span class="comment">/* module_init module_exit */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kernel.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/module.h&gt;</span> <span class="comment">/* MODULE_LICENSE */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kobject.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/slab.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./timestamp_autogenerated.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./version_autogenerated.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./sdrv_common.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// #undef PRT</span></span><br><span class="line"><span class="comment">// #undef PRTE</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PRT</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRT printk</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PRTE</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRTE printk</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> __<span class="title">SKOBJ_</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> skobj_name[<span class="number">32</span>];</span><br><span class="line">    <span class="type">char</span> name_attr[<span class="number">32</span>];</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> <span class="title">kobj</span>;</span></span><br><span class="line">&#125;<span class="type">skobj_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">skobj_t</span> *skobject1 = <span class="literal">NULL</span>; <span class="comment">// 定义kobject指针变量：skobject1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  dynamic_kobj_release()</span></span><br><span class="line"><span class="comment"> * @note   定义kobject的释放函数</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">dynamic_kobj_release</span><span class="params">(<span class="keyword">struct</span> kobject *kobj)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line">    PRT(<span class="string">&quot;kobject: (%p) \n&quot;</span>, kobj);</span><br><span class="line">    kfree(pskobj);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_show()</span></span><br><span class="line"><span class="comment"> * @note   读属性操作函数, cat 属性文件时调用这个函数，会把对应的值显示到终端</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> attribute *attr, <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">ssize_t</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">strcmp</span>(attr-&gt;name, <span class="string">&quot;skobj_attr_name&quot;</span>) == <span class="number">0</span>) <span class="comment">// skobj_attr_name 属性对应 pskobj-&gt;name 成员</span></span><br><span class="line">    &#123;</span><br><span class="line">        count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, pskobj-&gt;name_attr);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(<span class="built_in">strcmp</span>(attr-&gt;name, <span class="string">&quot;skobj_attr_data&quot;</span>) == <span class="number">0</span>) <span class="comment">// skobj_attr_data 属性对应 pskobj-&gt;data 成员</span></span><br><span class="line">    &#123;</span><br><span class="line">        count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%d\n&quot;</span>, pskobj-&gt;data);</span><br><span class="line">    &#125;</span><br><span class="line">    PRT(<span class="string">&quot;attr-&gt;name=%s count=%d\n&quot;</span>, attr-&gt;name, count);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_store()</span></span><br><span class="line"><span class="comment"> * @note   写属性操作文件, echo 属性文件时调用这个函数，用于echo x 中的 x 写入到属性对应的成员变量中</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_store</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> attribute *attr, <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line"></span><br><span class="line">    PRT(<span class="string">&quot;attr-&gt;name=%s count=%d\n&quot;</span>, attr-&gt;name, count);</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">strcmp</span>(attr-&gt;name, <span class="string">&quot;skobj_attr_name&quot;</span>) == <span class="number">0</span>)      <span class="comment">// skobj_attr_name 属性对应 pskobj-&gt;name 成员</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sscanf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, pskobj-&gt;name_attr);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span>(<span class="built_in">strcmp</span>(attr-&gt;name, <span class="string">&quot;skobj_attr_data&quot;</span>) == <span class="number">0</span>) <span class="comment">// skobj_attr_data 属性对应 pskobj-&gt;data 成员</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sscanf</span>(buf, <span class="string">&quot;%d\n&quot;</span>, &amp;pskobj-&gt;data);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义的 sysfs_ops 结构体，包含 show 和 store 函数指</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sysfs_ops</span> <span class="title">attr_sysfs_ops</span> =</span> &#123;</span><br><span class="line">    .show  = skobj_attr_show,</span><br><span class="line">    .store = skobj_attr_store,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 每一个注册的 attribute 结构都是一个属性文件，这里创建两个属性文件</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> <span class="title">s_attr_name</span> =</span> &#123;</span><br><span class="line">    .name = <span class="string">&quot;skobj_attr_name&quot;</span>,</span><br><span class="line">    .mode = S_IRWXUGO,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> <span class="title">s_attr_data</span> =</span> &#123;</span><br><span class="line">    .name = <span class="string">&quot;skobj_attr_data&quot;</span>,</span><br><span class="line">    .mode = S_IRWXUGO,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义一个指针数组，将 attribute 对象全部放入数组中，方便后续赋值给 ktype 的 default_attrs 成员</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> *<span class="title">s_attr</span>[] =</span> &#123;</span><br><span class="line">    &amp;s_attr_name, </span><br><span class="line">    &amp;s_attr_data, </span><br><span class="line">    <span class="literal">NULL</span>, </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义的 kobj_type 结构体，包含释放函数、默认属性和 sysfs_ops</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_type</span> <span class="title">stype</span> =</span> &#123;</span><br><span class="line">    .release = dynamic_kobj_release,</span><br><span class="line">    .sysfs_ops = &amp;attr_sysfs_ops,</span><br><span class="line">    .default_attrs = s_attr,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  sdriver_demo_init()</span></span><br><span class="line"><span class="comment"> * @note   </span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> __init <span class="type">int</span> <span class="title function_">sdriver_demo_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">	printk(<span class="string">&quot;*** [%s:%d]Build Time: %s %s, git version:%s ***\n&quot;</span>, __FUNCTION__,</span><br><span class="line">           __LINE__, KERNEL_KO_DATE, KERNEL_KO_TIME, KERNEL_KO_VERSION);</span><br><span class="line">    PRT(<span class="string">&quot;sdriver_demo module init!\n&quot;</span>);</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建kobject的第二种方法</span></span><br><span class="line">    <span class="comment">// 1.使用kzalloc函数分配了一个kobject对象的内存</span></span><br><span class="line">    skobject1 = kzalloc(<span class="keyword">sizeof</span>(<span class="type">skobj_t</span>), GFP_KERNEL);</span><br><span class="line">    <span class="keyword">if</span>(!skobject1)</span><br><span class="line">    &#123;</span><br><span class="line">        ret = <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">goto</span> err_kzalloc;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">snprintf</span>(skobject1-&gt;skobj_name, <span class="keyword">sizeof</span>(skobject1-&gt;skobj_name), <span class="string">&quot;skobject1&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.初始化并添加到内核中，名为&quot;skobject1&quot;。</span></span><br><span class="line">    ret = kobject_init_and_add(&amp;skobject1-&gt;kobj, &amp;stype, <span class="literal">NULL</span>, <span class="string">&quot;%s&quot;</span>, skobject1-&gt;skobj_name);</span><br><span class="line">    <span class="keyword">if</span>(ret &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        PRTE(<span class="string">&quot;kobject_init_and_add fail!ret=%d\n&quot;</span>, ret);</span><br><span class="line">        <span class="keyword">goto</span> err_kobject_init_and_add;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">err_kobject_init_and_add:</span><br><span class="line">    kfree(skobject1);</span><br><span class="line">err_kzalloc:</span><br><span class="line">	<span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  sdriver_demo_exit()</span></span><br><span class="line"><span class="comment"> * @note   </span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> __exit <span class="type">void</span> <span class="title function_">sdriver_demo_exit</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">// 释放之前创建的kobject对象</span></span><br><span class="line">    kobject_put(&amp;skobject1-&gt;kobj);</span><br><span class="line">    PRT(<span class="string">&quot;sdriver_demo module exit!\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">module_init(sdriver_demo_init); <span class="comment">// 将__init定义的函数指定为驱动的入口函数</span></span><br><span class="line">module_exit(sdriver_demo_exit); <span class="comment">// 将__exit定义的函数指定为驱动的出口函数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 模块信息(通过 modinfo chrdev_led_demo 查看) */</span></span><br><span class="line">MODULE_LICENSE(<span class="string">&quot;GPL v2&quot;</span>);            <span class="comment">/* 源码的许可证协议 */</span></span><br><span class="line">MODULE_AUTHOR(<span class="string">&quot;sumu&quot;</span>);               <span class="comment">/* 字符串常量内容为模块作者说明 */</span></span><br><span class="line">MODULE_DESCRIPTION(<span class="string">&quot;Description&quot;</span>);   <span class="comment">/* 字符串常量内容为模块功能说明 */</span></span><br><span class="line">MODULE_ALIAS(<span class="string">&quot;module&#x27;s other name&quot;</span>); <span class="comment">/* 字符串常量内容为模块别名 */</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
              </div>
            </details>

<h3 id="2-2-开发板验证"><a href="#2-2-开发板验证" class="headerlink" title="2.2 开发板验证"></a><font size=3>2.2 开发板验证</font></h3><p>只是改变了属性文件创建的逻辑，现象和上面完全一样。</p>
<h2 id="3-通过-ATTR-宏优化一下"><a href="#3-通过-ATTR-宏优化一下" class="headerlink" title="3. 通过__ATTR() 宏优化一下"></a><font size=3>3. 通过<code>__ATTR()</code> 宏优化一下</font></h2><h3 id="3-1-怎么优化？"><a href="#3-1-怎么优化？" class="headerlink" title="3.1 怎么优化？"></a><font size=3>3.1 怎么优化？</font></h3><p>看一下上面的show和store函数，这里以show函数为例：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250107172843740.png" alt="image-20250107172843740"  />

<p>由于操作不同属性的时候，都是调用的这个函数，我们要在这里面通过if或者switch来判断，一两个属性还好，七八个怎么办？是不是可以让<strong>一个属性对应一个show和store</strong>，这样每个属性都有自己的show&#x2F;store函数，这样属性再多也没啥问题了。</p>
<p>所以，这个时候我们就可以用的到 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/kobject.h#L163">struct kobj_attribute</a> 结构体，和 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L101">__ATTR()</a> 宏。需要注意以下几点：</p>
<ul>
<li>（1）需要定义 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L214">struct sysfs_ops</a> 类型的变量用于操作kobject的所有属性，也就是要实现下面两个函数：</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sysfs_ops</span> &#123;</span></span><br><span class="line">	<span class="type">ssize_t</span>	(*show)(<span class="keyword">struct</span> kobject *, <span class="keyword">struct</span> attribute *, <span class="type">char</span> *);</span><br><span class="line">	<span class="type">ssize_t</span>	(*store)(<span class="keyword">struct</span> kobject *, <span class="keyword">struct</span> attribute *, <span class="type">const</span> <span class="type">char</span> *, <span class="type">size_t</span>);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<ul>
<li>（2）需要定义每个属性的操作函数，就是实现 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/kobject.h#L163">struct kobj_attribute</a>  中的两个函数：</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> &#123;</span></span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> <span class="title">attr</span>;</span></span><br><span class="line">	<span class="type">ssize_t</span> (*show)(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr,</span><br><span class="line">			<span class="type">char</span> *buf);</span><br><span class="line">	<span class="type">ssize_t</span> (*store)(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr,</span><br><span class="line">			 <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>其实就很简单的一个逻辑，之前是靠if来区分不同的属性，现在改成通过container_of()函数在show&#x2F;store函数中获取不同的属性变量的地址，然后调用他们各自的show&#x2F;store函数。具体后面可以看demo。</p>
<h3 id="3-2-demo源码"><a href="#3-2-demo源码" class="headerlink" title="3.2 demo源码"></a><font size=3>3.2 demo源码</font></h3><p><a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/tree/master/05_device_model/08_attr_file_demo3">05_device_model&#x2F;08_attr_file_demo3 · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a></p>
<details class="folding-tag" blue><summary> 点击查看详情 </summary>
              <div class='content'>
              <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/init.h&gt;</span> <span class="comment">/* module_init module_exit */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kernel.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/module.h&gt;</span> <span class="comment">/* MODULE_LICENSE */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kobject.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/slab.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./timestamp_autogenerated.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./version_autogenerated.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./sdrv_common.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// #undef PRT</span></span><br><span class="line"><span class="comment">// #undef PRTE</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PRT</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRT printk</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PRTE</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRTE printk</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> __<span class="title">SKOBJ_</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> skobj_name[<span class="number">32</span>];</span><br><span class="line">    <span class="type">char</span> name_attr[<span class="number">32</span>];</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> <span class="title">kobj</span>;</span></span><br><span class="line">&#125;<span class="type">skobj_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">skobj_t</span> *skobject1 = <span class="literal">NULL</span>; <span class="comment">// 定义kobject指针变量：skobject1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_name_show()</span></span><br><span class="line"><span class="comment"> * @note   读属性操作函数, cat 属性文件时调用这个函数，会把对应的值显示到终端</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_name_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">ssize_t</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line">    count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, pskobj-&gt;name_attr);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_name_store()</span></span><br><span class="line"><span class="comment"> * @note   写属性操作文件, echo 属性文件时调用这个函数，用于echo x 中的 x 写入到属性对应的成员变量中</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_name_store</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line">    <span class="built_in">sscanf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, pskobj-&gt;name_attr);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_data_show()</span></span><br><span class="line"><span class="comment"> * @note   读属性操作函数, cat 属性文件时调用这个函数，会把对应的值显示到终端</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_data_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">ssize_t</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line">    count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%d\n&quot;</span>, pskobj-&gt;data);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_data_store()</span></span><br><span class="line"><span class="comment"> * @note   写属性操作文件, echo 属性文件时调用这个函数，用于echo x 中的 x 写入到属性对应的成员变量中</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_data_store</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line">    <span class="built_in">sscanf</span>(buf, <span class="string">&quot;%d\n&quot;</span>, &amp;pskobj-&gt;data);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义两个属性，用于管理 name_attr 和 data</span></span><br><span class="line"><span class="comment">// 注意，这里的mode赋值的时候不能用这些代表权限的宏，例如S_IRWXUGO，否则会在展开的时候出现问题</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> <span class="title">s_attr_name</span> =</span> __ATTR(skobj_attr_name, <span class="number">0664</span>, skobj_attr_name_show, skobj_attr_name_store);</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> <span class="title">s_attr_data</span> =</span> __ATTR(skobj_attr_data, <span class="number">0664</span>, skobj_attr_data_show, skobj_attr_data_store);</span><br><span class="line"><span class="comment">// 定义一个指针数组，将 attribute 对象全部放入数组中，方便后续赋值给 ktype 的 default_attrs 成员</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> *<span class="title">s_attr</span>[] =</span> &#123;</span><br><span class="line">    &amp;s_attr_name.attr, </span><br><span class="line">    &amp;s_attr_data.attr, </span><br><span class="line">    <span class="literal">NULL</span>, </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  dynamic_kobj_release()</span></span><br><span class="line"><span class="comment"> * @note   定义kobject的释放函数</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> <span class="type">void</span> <span class="title function_">dynamic_kobj_release</span><span class="params">(<span class="keyword">struct</span> kobject *kobj)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line">    PRT(<span class="string">&quot;kobject: (%p) \n&quot;</span>, kobj);</span><br><span class="line">    kfree(pskobj);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_show()</span></span><br><span class="line"><span class="comment"> * @note   读属性操作函数, cat 属性文件时调用这个函数，会把对应的值显示到终端</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> attribute *attr, <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">ssize_t</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> *<span class="title">pskobj_attr</span> =</span> container_of(attr, <span class="keyword">struct</span> kobj_attribute, attr);</span><br><span class="line">    count = pskobj_attr-&gt;show(kobj, pskobj_attr, buf);</span><br><span class="line">    PRT(<span class="string">&quot;attr-&gt;name=%s count=%d\n&quot;</span>, attr-&gt;name, count);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_store()</span></span><br><span class="line"><span class="comment"> * @note   写属性操作文件, echo 属性文件时调用这个函数，用于echo x 中的 x 写入到属性对应的成员变量中</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_store</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> attribute *attr, <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> *<span class="title">pskobj_attr</span> =</span> container_of(attr, <span class="keyword">struct</span> kobj_attribute, attr);</span><br><span class="line">    PRT(<span class="string">&quot;attr-&gt;name=%s count=%d\n&quot;</span>, attr-&gt;name, count);</span><br><span class="line">    <span class="keyword">return</span> pskobj_attr-&gt;store(kobj, pskobj_attr, buf, count);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义的 sysfs_ops 结构体，包含 show 和 store 函数指</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">sysfs_ops</span> <span class="title">attr_sysfs_ops</span> =</span> &#123;</span><br><span class="line">    .show  = skobj_attr_show,</span><br><span class="line">    .store = skobj_attr_store,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 自定义的 kobj_type 结构体，包含释放函数、默认属性和 sysfs_ops</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_type</span> <span class="title">stype</span> =</span> &#123;</span><br><span class="line">    .release = dynamic_kobj_release,</span><br><span class="line">    .sysfs_ops = &amp;attr_sysfs_ops,</span><br><span class="line">    .default_attrs = s_attr,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  sdriver_demo_init()</span></span><br><span class="line"><span class="comment"> * @note   </span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> __init <span class="type">int</span> <span class="title function_">sdriver_demo_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">	printk(<span class="string">&quot;*** [%s:%d]Build Time: %s %s, git version:%s ***\n&quot;</span>, __FUNCTION__,</span><br><span class="line">           __LINE__, KERNEL_KO_DATE, KERNEL_KO_TIME, KERNEL_KO_VERSION);</span><br><span class="line">    PRT(<span class="string">&quot;sdriver_demo module init!\n&quot;</span>);</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建kobject的第二种方法</span></span><br><span class="line">    <span class="comment">// 1.使用kzalloc函数分配了一个kobject对象的内存</span></span><br><span class="line">    skobject1 = kzalloc(<span class="keyword">sizeof</span>(<span class="type">skobj_t</span>), GFP_KERNEL);</span><br><span class="line">    <span class="keyword">if</span>(!skobject1)</span><br><span class="line">    &#123;</span><br><span class="line">        ret = <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">goto</span> err_kzalloc;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">snprintf</span>(skobject1-&gt;skobj_name, <span class="keyword">sizeof</span>(skobject1-&gt;skobj_name), <span class="string">&quot;skobject1&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2.初始化并添加到内核中，名为&quot;skobject1&quot;。</span></span><br><span class="line">    ret = kobject_init_and_add(&amp;skobject1-&gt;kobj, &amp;stype, <span class="literal">NULL</span>, <span class="string">&quot;%s&quot;</span>, skobject1-&gt;skobj_name);</span><br><span class="line">    <span class="keyword">if</span>(ret &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        PRTE(<span class="string">&quot;kobject_init_and_add fail!ret=%d\n&quot;</span>, ret);</span><br><span class="line">        <span class="keyword">goto</span> err_kobject_init_and_add;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">err_kobject_init_and_add:</span><br><span class="line">    kfree(skobject1);</span><br><span class="line">err_kzalloc:</span><br><span class="line">	<span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  sdriver_demo_exit()</span></span><br><span class="line"><span class="comment"> * @note   </span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> __exit <span class="type">void</span> <span class="title function_">sdriver_demo_exit</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">// 释放之前创建的kobject对象</span></span><br><span class="line">    kobject_put(&amp;skobject1-&gt;kobj);</span><br><span class="line">    PRT(<span class="string">&quot;sdriver_demo module exit!\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">module_init(sdriver_demo_init); <span class="comment">// 将__init定义的函数指定为驱动的入口函数</span></span><br><span class="line">module_exit(sdriver_demo_exit); <span class="comment">// 将__exit定义的函数指定为驱动的出口函数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 模块信息(通过 modinfo chrdev_led_demo 查看) */</span></span><br><span class="line">MODULE_LICENSE(<span class="string">&quot;GPL v2&quot;</span>);            <span class="comment">/* 源码的许可证协议 */</span></span><br><span class="line">MODULE_AUTHOR(<span class="string">&quot;sumu&quot;</span>);               <span class="comment">/* 字符串常量内容为模块作者说明 */</span></span><br><span class="line">MODULE_DESCRIPTION(<span class="string">&quot;Description&quot;</span>);   <span class="comment">/* 字符串常量内容为模块功能说明 */</span></span><br><span class="line">MODULE_ALIAS(<span class="string">&quot;module&#x27;s other name&quot;</span>); <span class="comment">/* 字符串常量内容为模块别名 */</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
              </div>
            </details>

<h3 id="3-3-开发板验证"><a href="#3-3-开发板验证" class="headerlink" title="3.3 开发板验证"></a><font size=3>3.3 开发板验证</font></h3><p>这里还是只是改了属性创建的写法，现象和验证步骤和前面完全一样。</p>
<h2 id="4-默认属性？"><a href="#4-默认属性？" class="headerlink" title="4. 默认属性？"></a><font size=3>4. 默认属性？</font></h2><h3 id="4-1-有一种创建方式没有指定ktype？"><a href="#4-1-有一种创建方式没有指定ktype？" class="headerlink" title="4.1 有一种创建方式没有指定ktype？"></a><font size=3>4.1 有一种创建方式没有指定ktype？</font></h3><p>我们知道kobject创建的时候有两种方式，一种是手动申请内存，然后调用<a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/lib/kobject.c#L434">kobject_init_and_add()</a> 函数来创建kobject：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">kobject_init_and_add</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_type *ktype,</span></span><br><span class="line"><span class="params">			 <span class="keyword">struct</span> kobject *parent, <span class="type">const</span> <span class="type">char</span> *fmt, ...)</span>;</span><br></pre></td></tr></table></figure>

<p>可以看到，这里可以直接指定ktype，这个时候我们可以很方便的给ktype添加属性。转头看向另一种创建方式，是自动申请内存，调用 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/lib/kobject.c#L748">kobject_create_and_add()</a> 函数完成kobject的创建：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> kobject *<span class="title function_">kobject_create_and_add</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *name, <span class="keyword">struct</span> kobject *parent)</span>;</span><br></pre></td></tr></table></figure>

<p>仔细一看，发现这个函数好像没有ktyp参数啊，那它的属性怎么办？通过这种方式创建 kobject 的时候，默认设置 kobj_type 的值为 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/lib/kobject.c#L719">dynamic_kobj_ktype</a>（在 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/lib/kobject.c#L743">kobject_create()</a> 函数中设置的）：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">kobj_type</span> <span class="title">dynamic_kobj_ktype</span> =</span> &#123;</span><br><span class="line">	.release	= dynamic_kobj_release,</span><br><span class="line">	.sysfs_ops	= &amp;kobj_sysfs_ops,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>对于属性的操作，主要是 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/lib/kobject.c#L815">kobj_sysfs_ops</a> ：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">sysfs_ops</span> <span class="title">kobj_sysfs_ops</span> =</span> &#123;</span><br><span class="line">	.show	= kobj_attr_show,</span><br><span class="line">	.store	= kobj_attr_store,</span><br><span class="line">&#125;;</span><br><span class="line">EXPORT_SYMBOL_GPL(kobj_sysfs_ops);</span><br></pre></td></tr></table></figure>

<p>可以看到最后调用了 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/lib/kobject.c#L791">kobj_attr_show()</a> 和 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/lib/kobject.c#L803">kobj_attr_store()</a>，这两个函数定义如下：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* default kobject attribute operations */</span></span><br><span class="line"><span class="type">static</span> <span class="type">ssize_t</span> <span class="title function_">kobj_attr_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> attribute *attr,</span></span><br><span class="line"><span class="params">			      <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> *<span class="title">kattr</span>;</span></span><br><span class="line">	<span class="type">ssize_t</span> ret = -EIO;</span><br><span class="line"></span><br><span class="line">	kattr = container_of(attr, <span class="keyword">struct</span> kobj_attribute, attr);</span><br><span class="line">	<span class="keyword">if</span> (kattr-&gt;show)</span><br><span class="line">		ret = kattr-&gt;show(kobj, kattr, buf);</span><br><span class="line">	<span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">static</span> <span class="type">ssize_t</span> <span class="title function_">kobj_attr_store</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> attribute *attr,</span></span><br><span class="line"><span class="params">			       <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> *<span class="title">kattr</span>;</span></span><br><span class="line">	<span class="type">ssize_t</span> ret = -EIO;</span><br><span class="line"></span><br><span class="line">	kattr = container_of(attr, <span class="keyword">struct</span> kobj_attribute, attr);</span><br><span class="line">	<span class="keyword">if</span> (kattr-&gt;store)</span><br><span class="line">		ret = kattr-&gt;store(kobj, kattr, buf, count);</span><br><span class="line">	<span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>看到这里，大概就知道了，这里其实 调用了 container_of() 来计算 attr成员所在的结构体的地址，然后调用里面的show&#x2F;store函数，这是不是和前面<code>__ATTR()</code>宏的哪个demo很像？只不过这里我们需要手动创建属性文件，并绑定每个属性对应的show&#x2F;store函数。</p>
<h3 id="4-2-demo源码"><a href="#4-2-demo源码" class="headerlink" title="4.2 demo源码"></a><font size=3>4.2 demo源码</font></h3><p><a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/tree/master/05_device_model/09_attr_file_demo4">05_device_model&#x2F;09_attr_file_demo4 · 苏木&#x2F;imx6ull-driver-demo - 码云 - 开源中国</a></p>
<details class="folding-tag" blue><summary> 点击查看详情 </summary>
              <div class='content'>
              <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/init.h&gt;</span> <span class="comment">/* module_init module_exit */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kernel.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/module.h&gt;</span> <span class="comment">/* MODULE_LICENSE */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kobject.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/slab.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./timestamp_autogenerated.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./version_autogenerated.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./sdrv_common.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// #undef PRT</span></span><br><span class="line"><span class="comment">// #undef PRTE</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PRT</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRT printk</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PRTE</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRTE printk</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> *<span class="title">skobject1</span> =</span> <span class="literal">NULL</span>; <span class="comment">// 定义kobject指针变量：skobject1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_name_show()</span></span><br><span class="line"><span class="comment"> * @note   读属性操作函数, cat 属性文件时调用这个函数，会把对应的值显示到终端</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_name_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">ssize_t</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="type">char</span> data[<span class="number">32</span>] = <span class="string">&quot;aaa&quot;</span>;</span><br><span class="line">    count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, data);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_name_store()</span></span><br><span class="line"><span class="comment"> * @note   写属性操作文件, echo 属性文件时调用这个函数，用于echo x 中的 x 写入到属性对应的成员变量中</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_name_store</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">char</span> data[<span class="number">32</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">    <span class="built_in">sscanf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, data);</span><br><span class="line">    PRT(<span class="string">&quot;data=%s\n&quot;</span>, data);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_data_show()</span></span><br><span class="line"><span class="comment"> * @note   读属性操作函数, cat 属性文件时调用这个函数，会把对应的值显示到终端</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_data_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">ssize_t</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">8</span>;</span><br><span class="line">    count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%d\n&quot;</span>, a);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_data_store()</span></span><br><span class="line"><span class="comment"> * @note   写属性操作文件, echo 属性文件时调用这个函数，用于echo x 中的 x 写入到属性对应的成员变量中</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_data_store</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> a = <span class="number">6</span>;</span><br><span class="line">    <span class="built_in">sscanf</span>(buf, <span class="string">&quot;%d\n&quot;</span>, &amp;a);</span><br><span class="line">    PRT(<span class="string">&quot;a = %d\n&quot;</span>, a);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义两个属性，用于管理 name_attr 和 data</span></span><br><span class="line"><span class="comment">// 注意，这里的mode赋值的时候不能用这些代表权限的宏，例如S_IRWXUGO，否则会在展开的时候出现问题</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> <span class="title">s_attr_name</span> =</span> __ATTR(skobj_attr_name, <span class="number">0664</span>, skobj_attr_name_show, skobj_attr_name_store);</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> <span class="title">s_attr_data</span> =</span> __ATTR(skobj_attr_data, <span class="number">0664</span>, skobj_attr_data_show, skobj_attr_data_store);</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  sdriver_demo_init()</span></span><br><span class="line"><span class="comment"> * @note   </span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> __init <span class="type">int</span> <span class="title function_">sdriver_demo_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">	printk(<span class="string">&quot;*** [%s:%d]Build Time: %s %s, git version:%s ***\n&quot;</span>, __FUNCTION__,</span><br><span class="line">           __LINE__, KERNEL_KO_DATE, KERNEL_KO_TIME, KERNEL_KO_VERSION);</span><br><span class="line">    PRT(<span class="string">&quot;sdriver_demo module init!\n&quot;</span>);</span><br><span class="line">    </span><br><span class="line">    skobject1 = kobject_create_and_add(<span class="string">&quot;skobject1&quot;</span>, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    ret = sysfs_create_file(skobject1, &amp;s_attr_name.attr);</span><br><span class="line">    <span class="keyword">if</span>(ret &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        PRTE(<span class="string">&quot;sysfs_create_file s_attr_name fail!ret=%d\n&quot;</span>, ret);</span><br><span class="line">        <span class="keyword">goto</span> err_sysfs_create_file_1;</span><br><span class="line">    &#125;</span><br><span class="line">    ret = sysfs_create_file(skobject1, &amp;s_attr_data.attr);</span><br><span class="line">    <span class="keyword">if</span>(ret &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        PRTE(<span class="string">&quot;sysfs_create_file s_attr_data fail!ret=%d\n&quot;</span>, ret);</span><br><span class="line">        <span class="keyword">goto</span> err_sysfs_create_file_2;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">err_sysfs_create_file_2:</span><br><span class="line">    sysfs_remove_file(skobject1, &amp;s_attr_name.attr);</span><br><span class="line">err_sysfs_create_file_1:</span><br><span class="line">    kobject_put(skobject1);</span><br><span class="line">	<span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  sdriver_demo_exit()</span></span><br><span class="line"><span class="comment"> * @note   </span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> __exit <span class="type">void</span> <span class="title function_">sdriver_demo_exit</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">// 释放之前创建的kobject对象</span></span><br><span class="line">    kobject_put(skobject1);</span><br><span class="line">    PRT(<span class="string">&quot;sdriver_demo module exit!\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">module_init(sdriver_demo_init); <span class="comment">// 将__init定义的函数指定为驱动的入口函数</span></span><br><span class="line">module_exit(sdriver_demo_exit); <span class="comment">// 将__exit定义的函数指定为驱动的出口函数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 模块信息(通过 modinfo chrdev_led_demo 查看) */</span></span><br><span class="line">MODULE_LICENSE(<span class="string">&quot;GPL v2&quot;</span>);            <span class="comment">/* 源码的许可证协议 */</span></span><br><span class="line">MODULE_AUTHOR(<span class="string">&quot;sumu&quot;</span>);               <span class="comment">/* 字符串常量内容为模块作者说明 */</span></span><br><span class="line">MODULE_DESCRIPTION(<span class="string">&quot;Description&quot;</span>);   <span class="comment">/* 字符串常量内容为模块功能说明 */</span></span><br><span class="line">MODULE_ALIAS(<span class="string">&quot;module&#x27;s other name&quot;</span>); <span class="comment">/* 字符串常量内容为模块别名 */</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
              </div>
            </details>

<h3 id="4-3-开发板验证"><a href="#4-3-开发板验证" class="headerlink" title="4.3 开发板验证"></a><font size=3>4.3 开发板验证</font></h3><p>这个demo的现象与前面的不同，后面会分析为什么没有办法保持一致。（也许是我太菜了，没有想到好办法？）</p>
<ul>
<li>（1）一开始的 &#x2F;sys目录</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108104459756.png" alt="image-20250108104459756"  />

<ul>
<li>（2）加载模块</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108104653640.png" alt="image-20250108104653640"  />

<p>加载模块后，会发现，在&#x2F;sys目录生成了 skobject1 目录，在&#x2F;sys&#x2F;skobject1目录下有两个属性文件skobj_attr_name和skobj_attr_data。</p>
<ul>
<li>（3）查看skobj_attr_name和skobj_attr_data的值</li>
</ul>
<p>由于前面stow中数据是写死的，并且没有与内核中某些数据相关联，所以这里显示的值都是固定的：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108104908429.png" alt="image-20250108104908429"  />

<ul>
<li>（4）修改skobj_attr_name和skobj_attr_data的值</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108105043285.png" alt="image-20250108105043285"  />

<p>会发现这里修改之后，查看的话，内容没变，原因就是上面写死了，并没有与某个变量保持一致，其实还是可以的，定义成全局变量就行了，只是不能像前面的demo一样，把kobject和自己的属性数据封装到一起，后面会去分析为什么没有封装到一起做。</p>
<ul>
<li>（5）卸载模块</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108105249577.png" alt="image-20250108105249577"  />

<p>可以看到，卸载模块后，资源释放，两个属性文件以及kobject目录全都没了。</p>
<h3 id="4-4-为什么不能封装到一起？"><a href="#4-4-为什么不能封装到一起？" class="headerlink" title="4.4 为什么不能封装到一起？"></a><font size=3>4.4 为什么不能封装到一起？</font></h3><p>前面我们是这样来封装数据：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> __<span class="title">SKOBJ_</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> skobj_name[<span class="number">32</span>];</span><br><span class="line">    <span class="type">char</span> name_attr[<span class="number">32</span>];</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> <span class="title">kobj</span>;</span></span><br><span class="line">&#125;<span class="type">skobj_t</span>;</span><br></pre></td></tr></table></figure>

<p>这样我们可以很方便的为自己创建的kobj添加属性，并且比较好管理，总好过这样：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> __<span class="title">SKOBJ_</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> skobj_name[<span class="number">32</span>];</span><br><span class="line">    <span class="type">char</span> name_attr[<span class="number">32</span>];</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">&#125;<span class="type">skobj_t</span>;</span><br><span class="line"><span class="type">skobj_t</span> kobject_attr = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> *<span class="title">kobj</span>;</span></span><br></pre></td></tr></table></figure>

<p>然后去对应全局变量把，那么为什么  <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/lib/kobject.c#L748">kobject_create_and_add()</a> 自动创建的kobject对象不能像前面几个demo一样把属性和对象封装到一起？接下来就来分析一下为什么不行。</p>
<p>前面封装到一起的前提是我们可以通过内部的kobj成员，在show&#x2F;store中使用下面的方式获取到这个结构体的地址：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_name_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">ssize_t</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="type">skobj_t</span> *pskobj = container_of(kobj, <span class="type">skobj_t</span>, kobj);</span><br><span class="line">    count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, pskobj-&gt;name_attr);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>container_of()这个函数，在获取结构体的地址的时候，我们需要知道结构体的类型，还需要知道<strong>这个结构体中其中一个成员的地址</strong>，这个地址必须是成员的地址，若通过指针成员获取结构体地址的话，必须是指针自己的地址，而不是指针指向的地址。我们可以看一下上一节<code>__ATTR()</code> 宏优化过的那个 <a target="_blank" rel="noopener" href="https://gitee.com/sumumm/imx6ull-driver-demo/blob/master/05_device_model/08_attr_file_demo3/sdriver_demo.c">demo</a> 中，kobj地址的情况：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108151909652.png" alt="image-20250108151909652"  />

<blockquote>
<p>Tips：注意打印地址的时候，可以用%px，不然可能会打印出来个 ptrval ，好像是因为内核担心暴露地址啥的。</p>
</blockquote>
<p>我们来看一下 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/lib/kobject.c#L748">kobject_create_and_add()</a> 函数：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> kobject *<span class="title function_">kobject_create_and_add</span><span class="params">(<span class="type">const</span> <span class="type">char</span> *name, <span class="keyword">struct</span> kobject *parent)</span>;</span><br></pre></td></tr></table></figure>

<p>可以看到，调用它的时候，我们不需要自己申请内存，它的返回值是创建完成的kobject的地址。我们要做的就是定义一个指针指向这个地址，这样就可以把kobj往后传了。我们来看一下这种情况下kobj的地址情况：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108160633745.png" alt="image-20250108160633745"  />

<p>接下来对比一下这两种情况：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108161846238.png" alt="image-20250108161846238"  />

<p>看一下左边的逻辑，我们到那个dynamic_kobj_release()函数里面，获取到了kobj的地址，但是这里是变量的地址，它不是一个指针，并且<strong>这个地址就是是结构体成员 kobj 的地址</strong>，也可以这样理解：我们创建的kobject对象和其他的成员在同一个地方：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108162946044.png" alt="image-20250108162946044"  />

<p>对于右侧的方式，我们也把kobject的地址传递到了 skobj_attr_name_show()函数，这里没有问题，可以正常使用kobject对象的，但是需要注意的是，这里传递的是kobject的地址，整个地址是存放在一个指针里面，它俩不是同一个东西，按照右侧这种方式，我们封装成结构体，应该是这样的：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> __<span class="title">SKOBJ_</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> skobj_name[<span class="number">32</span>];</span><br><span class="line">    <span class="type">char</span> name_attr[<span class="number">32</span>];</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> *<span class="title">kobj</span>;</span></span><br><span class="line">&#125;<span class="type">skobj_t</span>;</span><br></pre></td></tr></table></figure>

<p>就会出现这种情况：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108163730977.png" alt="image-20250108163730977"  />

<p>最后，传过去的其实是kobj这个成员指针指向的的地址，毫无疑问，它确实是kobject对象的地址，但是我们为了获取整个结构体的地址，我们需要的知道的是<strong>结构体成员的地址，而非指针指向的地址</strong>。所以，这里就出现了问题，kobj指针自己的地址是没有继续向下传递的，传递的都是kobj指针指向的kobject对象的地址。这就意味这无法在skobj_attr_name_show()函数中通过kobject的地址使用container_of()从而获取整个结构体的地址。</p>
<h2 id="5-创建多个属性的简单方式"><a href="#5-创建多个属性的简单方式" class="headerlink" title="5. 创建多个属性的简单方式"></a><font size=3>5. 创建多个属性的简单方式</font></h2><h3 id="5-1-sysfs-create-group"><a href="#5-1-sysfs-create-group" class="headerlink" title="5.1 sysfs_create_group()"></a><font size=3>5.1 sysfs_create_group()</font></h3><p>多个属性的创建，我们可以使用 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/group.c#L160">sysfs_create_group()</a> 函数。我们需要创建一个属性文件数组，属性文件数组是一个以  <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L61">struct attribute_group *</a> 类型为元素的数组，以 NULL 结束。每个  <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/include/linux/sysfs.h#L61">struct attribute_group</a>  结构体表示一个属性文件，可以使用&amp;运算符将属性对象（如 struct kobject_attribute） 的.attr 字段传递给属性文件数组。</p>
<p>下面是一个使用 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/group.c#L160">sysfs_create_group()</a> 创建一个组并添加属性文件的简单demo：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> *<span class="title">attrs</span>[] =</span> &#123;</span><br><span class="line">	&amp;attr1.attr, </span><br><span class="line">    &amp;attr2.attr, </span><br><span class="line">    <span class="literal">NULL</span>, </span><br><span class="line">&#125;;</span><br><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">attribute_group</span> <span class="title">attr_group</span> =</span> &#123;</span><br><span class="line">	.name = <span class="string">&quot;my_group&quot;</span>, </span><br><span class="line">    .attrs = attrs, </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">sysfs_create_group(kobj, &amp;attr_group);</span><br></pre></td></tr></table></figure>

<p>我们创建了一个名叫“my_group”的组，并将属性文件 attr1 和 attr2 添加 到该组中，然后使用 <a target="_blank" rel="noopener" href="https://elixir.bootlin.com/linux/v4.19.71/source/fs/sysfs/group.c#L160">sysfs_create_group()</a>  将该组添加到指定的 kobject kobj 中。</p>
<h3 id="5-2-demo源码"><a href="#5-2-demo源码" class="headerlink" title="5.2 demo源码"></a><font size=3>5.2 demo源码</font></h3><details class="folding-tag" blue><summary> 点击查看详情 </summary>
              <div class='content'>
              <figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/init.h&gt;</span> <span class="comment">/* module_init module_exit */</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kernel.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/module.h&gt;</span> <span class="comment">/* MODULE_LICENSE */</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/kobject.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;linux/slab.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./timestamp_autogenerated.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./version_autogenerated.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;./sdrv_common.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// #undef PRT</span></span><br><span class="line"><span class="comment">// #undef PRTE</span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PRT</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRT printk</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">ifndef</span> PRTE</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PRTE printk</span></span><br><span class="line"><span class="meta">#<span class="keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> __<span class="title">SKOBJ_ATTR_VAR_</span>&#123;</span></span><br><span class="line">    <span class="type">char</span> skobj_name[<span class="number">32</span>];</span><br><span class="line">    <span class="type">char</span> name_attr[<span class="number">32</span>];</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">&#125;<span class="type">skobj_attr_var_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobject</span> *<span class="title">skobject1</span> =</span> <span class="literal">NULL</span>; <span class="comment">// 定义kobject指针变量：skobject1</span></span><br><span class="line"><span class="type">skobj_attr_var_t</span> skobject1_attr = &#123;<span class="number">0</span>&#125;;     <span class="comment">// skobject1的两个属性</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_name_show()</span></span><br><span class="line"><span class="comment"> * @note   读属性操作函数, cat 属性文件时调用这个函数，会把对应的值显示到终端</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_name_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">ssize_t</span> count = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">strcmp</span>(kobj-&gt;name, skobject1_attr.skobj_name) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, skobject1_attr.name_attr);</span><br><span class="line">    &#125;</span><br><span class="line">    PRT(<span class="string">&quot;kobj-&gt;name=%s attr-&gt;name=%s count=%d\n&quot;</span>, kobj-&gt;name, attr-&gt;attr.name, count);</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_name_store()</span></span><br><span class="line"><span class="comment"> * @note   写属性操作文件, echo 属性文件时调用这个函数，用于echo x 中的 x 写入到属性对应的成员变量中</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_name_store</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count)</span></span><br><span class="line">&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">strcmp</span>(kobj-&gt;name, skobject1_attr.skobj_name) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sscanf</span>(buf, <span class="string">&quot;%s\n&quot;</span>, skobject1_attr.name_attr);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    PRT(<span class="string">&quot;kobj-&gt;name=%s attr-&gt;name=%s count=%d\n&quot;</span>, kobj-&gt;name, attr-&gt;attr.name, count);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_data_show()</span></span><br><span class="line"><span class="comment"> * @note   读属性操作函数, cat 属性文件时调用这个函数，会把对应的值显示到终端</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_data_show</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">char</span> *buf)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">ssize_t</span> count = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">strcmp</span>(kobj-&gt;name, skobject1_attr.skobj_name) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        count = <span class="built_in">sprintf</span>(buf, <span class="string">&quot;%d\n&quot;</span>, skobject1_attr.data);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    PRT(<span class="string">&quot;kobj-&gt;name=%s attr-&gt;name=%s count=%d\n&quot;</span>, kobj-&gt;name, attr-&gt;attr.name, count);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  skobj_attr_data_store()</span></span><br><span class="line"><span class="comment"> * @note   写属性操作文件, echo 属性文件时调用这个函数，用于echo x 中的 x 写入到属性对应的成员变量中</span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">ssize_t</span> <span class="title function_">skobj_attr_data_store</span><span class="params">(<span class="keyword">struct</span> kobject *kobj, <span class="keyword">struct</span> kobj_attribute *attr, <span class="type">const</span> <span class="type">char</span> *buf, <span class="type">size_t</span> count)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="built_in">strcmp</span>(kobj-&gt;name, skobject1_attr.skobj_name) == <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="built_in">sscanf</span>(buf, <span class="string">&quot;%d\n&quot;</span>, &amp;skobject1_attr.data);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    PRT(<span class="string">&quot;kobj-&gt;name=%s attr-&gt;name=%s count=%d\n&quot;</span>, kobj-&gt;name, attr-&gt;attr.name, count);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义两个属性，用于管理 name_attr 和 data</span></span><br><span class="line"><span class="comment">// 注意，这里的mode赋值的时候不能用这些代表权限的宏，例如S_IRWXUGO，否则会在展开的时候出现问题</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> <span class="title">s_attr_name</span> =</span> __ATTR(skobj_attr_name, <span class="number">0664</span>, skobj_attr_name_show, skobj_attr_name_store);</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">kobj_attribute</span> <span class="title">s_attr_data</span> =</span> __ATTR(skobj_attr_data, <span class="number">0664</span>, skobj_attr_data_show, skobj_attr_data_store);</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">attribute</span> *<span class="title">s_attr</span>[] =</span> &#123;</span><br><span class="line">    &amp;s_attr_name.attr,</span><br><span class="line">    &amp;s_attr_data.attr,</span><br><span class="line">    <span class="literal">NULL</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">attribute_group</span> <span class="title">s_attr_group</span> =</span> &#123;</span><br><span class="line">    .name = <span class="string">&quot;skobj_attr&quot;</span>,</span><br><span class="line">    .attrs = s_attr,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  sdriver_demo_init()</span></span><br><span class="line"><span class="comment"> * @note   </span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> __init <span class="type">int</span> <span class="title function_">sdriver_demo_init</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> ret = <span class="number">0</span>;</span><br><span class="line">	printk(<span class="string">&quot;*** [%s:%d]Build Time: %s %s, git version:%s ***\n&quot;</span>, __FUNCTION__,</span><br><span class="line">           __LINE__, KERNEL_KO_DATE, KERNEL_KO_TIME, KERNEL_KO_VERSION);</span><br><span class="line">    PRT(<span class="string">&quot;sdriver_demo module init!\n&quot;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">snprintf</span>(skobject1_attr.skobj_name, <span class="keyword">sizeof</span>(skobject1_attr.skobj_name), <span class="string">&quot;skobject1&quot;</span>);</span><br><span class="line">    skobject1 = kobject_create_and_add(skobject1_attr.skobj_name, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在 kobject &quot;skobject1&quot; 中创建属性组</span></span><br><span class="line">    ret = sysfs_create_group(skobject1, &amp;s_attr_group);</span><br><span class="line">    <span class="keyword">if</span>(ret &lt; <span class="number">0</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        PRTE(<span class="string">&quot;sysfs_create_group fail!ret=%d\n&quot;</span>, ret);</span><br><span class="line">        <span class="keyword">goto</span> err_sysfs_create_group;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">err_sysfs_create_group:</span><br><span class="line">    kobject_put(skobject1);</span><br><span class="line">	<span class="keyword">return</span> ret;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @brief  sdriver_demo_exit()</span></span><br><span class="line"><span class="comment"> * @note   </span></span><br><span class="line"><span class="comment"> * @param [in]</span></span><br><span class="line"><span class="comment"> * @param [out]</span></span><br><span class="line"><span class="comment"> * @retval </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="type">static</span> __exit <span class="type">void</span> <span class="title function_">sdriver_demo_exit</span><span class="params">(<span class="type">void</span>)</span></span><br><span class="line">&#123;</span><br><span class="line">	<span class="comment">// 释放之前创建的kobject对象</span></span><br><span class="line">    kobject_put(skobject1);</span><br><span class="line">    PRT(<span class="string">&quot;sdriver_demo module exit!\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">module_init(sdriver_demo_init); <span class="comment">// 将__init定义的函数指定为驱动的入口函数</span></span><br><span class="line">module_exit(sdriver_demo_exit); <span class="comment">// 将__exit定义的函数指定为驱动的出口函数</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 模块信息(通过 modinfo chrdev_led_demo 查看) */</span></span><br><span class="line">MODULE_LICENSE(<span class="string">&quot;GPL v2&quot;</span>);            <span class="comment">/* 源码的许可证协议 */</span></span><br><span class="line">MODULE_AUTHOR(<span class="string">&quot;sumu&quot;</span>);               <span class="comment">/* 字符串常量内容为模块作者说明 */</span></span><br><span class="line">MODULE_DESCRIPTION(<span class="string">&quot;Description&quot;</span>);   <span class="comment">/* 字符串常量内容为模块功能说明 */</span></span><br><span class="line">MODULE_ALIAS(<span class="string">&quot;module&#x27;s other name&quot;</span>); <span class="comment">/* 字符串常量内容为模块别名 */</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
              </div>
            </details>

<h3 id="5-3-开发板验证"><a href="#5-3-开发板验证" class="headerlink" title="5.3 开发板验证"></a><font size=3>5.3 开发板验证</font></h3><p>我们编译完成后，拷贝到开发板，依次进行以下验证：</p>
<ul>
<li>（1）一开始的 &#x2F;sys目录</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108185243172.png" alt="image-20250108185243172"  />

<ul>
<li>（2）加载模块</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108185515880.png" alt="image-20250108185515880"  />

<p>加载模块后，会发现，在&#x2F;sys目录生成了 skobject1 目录，在&#x2F;sys&#x2F;skobject1目录下有一个skobj_attr目录，这个skobj_attr目录里面两个属性文件skobj_attr_name和skobj_attr_data。</p>
<ul>
<li>（3）查看&#x2F;修改skobj_attr_name和skobj_attr_data的值</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108185904025.png" alt="image-20250108185904025"  />

<ul>
<li>（5）卸载模块</li>
</ul>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/01%E5%B5%8C%E5%85%A5%E5%BC%8F%E5%BC%80%E5%8F%91/02IMX6ULL%E5%B9%B3%E5%8F%B0/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/LV06-04-linux%E8%AE%BE%E5%A4%87%E6%A8%A1%E5%9E%8B-05-%E5%B1%9E%E6%80%A7%E6%96%87%E4%BB%B6/img/image-20250108185957935.png" alt="image-20250108185957935"  />

<p>可以看到，卸载模块后，资源释放，属性文件以及kobject目录全都没了。</p>
<blockquote>
<p>参考资料：</p>
<p>【1】<a target="_blank" rel="noopener" href="https://www.cnblogs.com/xinghuo123/p/12871950.html">linux设备驱动(9)attribute详解 - Action_er - 博客园</a></p>
<p>【2】<a target="_blank" rel="noopener" href="https://blog.csdn.net/sty01z/article/details/130471250">linux驱动-设备驱动模型(属性文件 kobject )_linux设备模型 kobject-CSDN博客</a></p>
<p>【3】<a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_46999174/article/details/141607653">Linux驱动开发—设备模型框架 kobject创建属性文件_修改 kobject 创建的文件操作权限-CSDN博客</a></p>
<p>【4】<a target="_blank" rel="noopener" href="https://www.jianshu.com/p/7fb4b9349917">内核里printk打印指针打印出(<strong><strong>ptrval</strong></strong>) - 简书</a></p>
<p>【5】<a target="_blank" rel="noopener" href="https://www.kernel.org/doc/html/latest/translations/zh_CN/core-api/printk-formats.html">如何获得正确的printk格式占位符 — The Linux Kernel documentation</a></p>
</blockquote>

    </div>

    
    
    

    <footer class="post-footer">




    <div>
        
            <div style="text-align:center;color: #ccc;font-size:14px;">
            ----------本文结束
            <i class="fas fa-fan fa-spin" style="color: #FF1493; font-size: 1rem"></i>
            感谢您的阅读----------
            </div>
        
    </div>





  
  <div class="my_post_copyright"> 
    <p><span>文章标题:</span><a href="/post/3cca4963.html">LV06-04-linux设备模型-05-属性文件</a></p>
    <p><span>文章作者:</span><a href="/" title="欢迎访问 《苏木》 的学习笔记">苏木</a></p>
    <p><span>发布时间:</span>2025年01月08日 - 22:22</p>
    <p><span>最后更新:</span>2025年06月14日 - 00:25</p>
    <p><span>原始链接:</span><a href="/post/3cca4963.html" title="LV06-04-linux设备模型-05-属性文件">https://sumumm.github.io/post/3cca4963.html</a></p>
    <p><span>许可协议:</span><i class="fab fa-creative-commons"></i> <a rel="license" href= "https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p>  
  </div>
  


          <div class="post-tags">
              <a href="/tags/LV06-%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91/" rel="tag"><i class="fa fa-tag"></i> LV06-驱动开发</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/post/c0b48643.html" rel="prev" title="LV40-01-二维码识别-01-二维码基础">
                  <i class="fa fa-angle-left"></i> LV40-01-二维码识别-01-二维码基础
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/post/ea2f5d9c.html" rel="next" title="LV06-04-linux设备模型-04-kobject的释放">
                  LV06-04-linux设备模型-04-kobject的释放 <i class="fa fa-angle-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">

  <div class="copyright">
    &copy; 2017 – 
    <span itemprop="copyrightYear">2025</span>
    <span class="with-love">
      <i class="fa fa-heart"></i>
    </span>
    <span class="author" itemprop="copyrightHolder">苏木</span>
  </div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">3.7m</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">225:26</span>
  </span>
</div>




    <span id="sitetime"></span>
    <script defer language=javascript>
        function siteTime()
        {
            window.setTimeout("siteTime()", 1000);
            var seconds = 1000;
            var minutes = seconds * 60;
            var hours = minutes * 60;
            var days = hours * 24;
            var years = days * 365;
            var today = new Date();
            var todayYear = today.getFullYear();
            var todayMonth = today.getMonth()+1;
            var todayDate = today.getDate();
            var todayHour = today.getHours();
            var todayMinute = today.getMinutes();
            var todaySecond = today.getSeconds();
            /*==================================================
            Date.UTC() -- 返回date对象距世界标准时间(UTC)1970年1月1日午夜之间的毫秒数(时间戳)
            year        - 作为date对象的年份，为4位年份值
            month       - 0-11之间的整数，做为date对象的月份
            day         - 1-31之间的整数，做为date对象的天数
            hours       - 0(午夜24点)-23之间的整数，做为date对象的小时数
            minutes     - 0-59之间的整数，做为date对象的分钟数
            seconds     - 0-59之间的整数，做为date对象的秒数
            microseconds - 0-999之间的整数，做为date对象的毫秒数
            ==================================================*/
            var t1 = Date.UTC(2017, 
                              5, 
                              19, 
                              0, 
                              0, 
                              0); //北京时间
            var t2 = Date.UTC(todayYear,todayMonth,todayDate,todayHour,todayMinute,todaySecond);
            var diff = t2-t1;
            var diffYears = Math.floor(diff/years);
            var diffDays = Math.floor((diff/days)-diffYears*365);
            var diffHours = Math.floor((diff-(diffYears*365+diffDays)*days)/hours);
            var diffMinutes = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours)/minutes);
            var diffSeconds = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours-diffMinutes*minutes)/seconds);
            document.getElementById("sitetime").innerHTML="已在这里 "+diffYears+" 年 "+diffDays+" 天 "+diffHours+" 小时 "+diffMinutes+" 分钟 "+diffSeconds+" 秒";
        }
        siteTime();
    </script>



    </div>
  </footer>

  
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/next-theme-pjax/0.6.0/pjax.min.js" integrity="sha256-vxLn1tSKWD4dqbMRyv940UYw4sXgMtYcK6reefzZrao=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.umd.js" integrity="sha256-ytMJGN3toR+a84u7g7NuHm91VIR06Q41kMWDr2pq7Zo=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lozad.js/1.16.0/lozad.min.js" integrity="sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script><script src="/js/pjax.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>




  <script src="/js/third-party/fancybox.js"></script>

  <script src="/js/third-party/pace.js"></script>


  




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/js/third-party/math/mathjax.js"></script>


 
        <div id="click-show-text"
            data-mobile = false
            data-text = 富强,民主,文明,和谐,自由,平等,公正,法制,爱国,敬业,诚信,友善
            data-fontsize = 15px
            data-random= false>
        </div>
       

      
        <script async src=https://cdn.jsdelivr.net/npm/hexo-next-mouse-effect@latest/click/showText.js></script>
      

      
    




    <script async src="/js/fancybox_param.js"></script>





<!-- APlayer本体 -->



</body>
</html>
